[
  {
    "path": ".gitignore",
    "content": ".svn\n.bowerrc\nbuild\nbuild-doc\n.idea\nbower_components\nnode_modules\nnpm-debug.log\npackage-lock.json"
  },
  {
    "path": ".jshintrc",
    "content": "{\n    \"curly\": true,\n    \"eqeqeq\": true,\n    \"immed\": true,\n    \"latedef\": true,\n    \"newcap\": true,\n    \"noarg\": true,\n    \"sub\": true,\n    \"undef\": true,\n    \"unused\": true,\n    \"boss\": true,\n    \"eqnull\": true,\n    \"node\": true\n}\n\n"
  },
  {
    "path": "Gruntfile.js",
    "content": "'use strict';\nmodule.exports = function (grunt) {\n    require('load-grunt-tasks')(grunt);\n    require('time-grunt')(grunt);\n    grunt.initConfig({\n        pkg: grunt.file.readJSON('package.json'),\n        jshint: {\n            gruntfile: {\n                options: {\n                    jshintrc: '.jshintrc'\n                },\n                src: 'Gruntfile.js'\n            },\n            player: {\n                options: {\n                    jshintrc: 'src/.jshintrc'\n                },\n                src: [\n                    'src/player/**/*.js',\n                    'src/helpers/**/*.js',\n                    'src/plugins/**/*.js',\n                    'src/*.js'\n                ]\n            },\n            i18n: {\n                options: {\n                    jshintrc: 'src/.jshintrc'\n                },\n                src: [\n                    'src/i18n/**/*.js'\n                ]\n            },\n            // Configuration with console.log\n            log: {\n                options: {\n                    jshintrc: 'src/log/.jshintrc'\n                },\n                src: [\n                    'src/log/**/*.js'\n                ]\n            },\n            // Configuration with ok,notEqual,deepEqual,notDeepEqual...\n            tests: {\n                options: {\n                    jshintrc: 'tests/.jshintrc'\n                },\n                src: [\n                    'tests/**/*.js'\n                ]\n            }\n        },\n        uglify: {\n            options: {\n                banner: '/*!<%= pkg.description %> V<%= pkg.version %>, © <%= pkg.author %> <%= grunt.template.today(\"yyyy\") %> */\\n<%= pkg.licenseText %>',\n                compress: {\n                    global_defs: {\n                        \"_PlayerAmaliaVersion_\": '<%= pkg.description %> V<%= pkg.version %>, © <%= pkg.author %> <%= grunt.template.today(\"yyyy\") %>',\n                        \"_PlayerAmaliaHomepage_\": '<%= pkg.homepage %>'\n                    }\n                }\n            },\n            build: {\n                files: {\n                    'build/js/i18n/<%= pkg.name %>-message-en.js': [\n                        'src/i18n/player-error-message-en.js',\n                        'src/i18n/player-message-en.js'\n                    ],\n                    'build/js/<%= pkg.name %>-logger.min.js': [\n                        'src/log/log-handler.js'\n                    ],\n                    'build/js/<%= pkg.name %>.min.js': [\n                        'src/utils/jquery-class.js',\n                        'src/utils/jquery-knob.js',\n                        'src/utils/jquery.ui.touch-punch.js',\n                        'src/utils/dash.min.js',\n                        'src/utils/jquery-debouncedresize.js',\n                        'src/utils/raphael_free_transform.js',\n                        'src/utils/jquery.hotkeys.js',\n                        'src/helpers/browser-feature-detection.js',\n                        'src/helpers/html5-helper.js',\n                        'src/helpers/utilities-helper.js',\n                        'src/player/constants/player-error-code.js',\n                        'src/player/constants/player-event-type.js',\n                        'src/player/constants/player-message.js',\n                        'src/player/loader/base-loader.js',\n                        'src/player/loader/http-loader.js',\n                        'src/player/loader/ws-loader.js',\n                        'src/player/parsers/base-parser-metadata.js',\n                        'src/player/metadata/metadata-manager.js',\n                        'src/player/metadata/localisation-manager.js',\n                        'src/player/widgets/widget-base.js',\n                        'src/player/widgets/base-button.js',\n                        'src/player/widgets/time-label.js',\n                        'src/player/widgets/play-button.js',\n                        'src/player/widgets/pause-button.js',\n                        'src/player/widgets/fullscreen-button.js',\n                        'src/player/widgets/volume-control-bar.js',\n                        'src/player/widgets/progress-bar.js',\n                        'src/player/widgets/label.js',\n                        'src/player/widgets/jog-shuttle-button.js',\n                        'src/player/widgets/channel-volume-control-bar.js',\n                        'src/player/plugins/plugin-base.js',\n                        'src/player/plugins/plugin-base-multi-blocks.js',\n                        'src/player/plugins/plugin-base-simple-block.js',\n                        'src/player/plugins/plugin-binding-manager.js',\n                        'src/player/plugins/plugin-manager.js',\n                        'src/player/plugins/captions-base.js',\n                        'src/player/plugins/context-menu-plugin.js',\n                        'src/player/plugins/custom-control-bar.js',\n                        'src/player/local-storage-manager.js',\n                        'src/player/media-type-manager.js',\n                        'src/player/media-type-dash-mpeg.js',\n                        'src/player/shortcuts-manager.js',\n                        'src/player/base-player.js',\n                        'src/player/player-html5.js',\n                        'src/player/media-factory.js',\n                        'src/ina-player.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-watermark.min.js': [\n                        'src/plugins/watermark/watermark.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-overlay.min.js': [\n                        'src/plugins/overlay/spatials-data-parser.js',\n                        'src/plugins/overlay/draw-base.js',\n                        'src/plugins/overlay/draw-rect.js',\n                        'src/plugins/overlay/draw-ellipse.js',\n                        'src/plugins/overlay/overlay.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-text-sync.min.js': [\n                        'src/plugins/text-sync/component.js',\n                        'src/plugins/text-sync/text-sync.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-captions.min.js': [\n                        'src/plugins/captions/captions.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-timeline.min.js': [\n                        'src/plugins/timeline/default-configuration.js',\n                        'src/plugins/timeline/base-component.js',\n                        'src/plugins/timeline/focus-component.js',\n                        'src/plugins/timeline/tic-component.js',\n                        'src/plugins/timeline/zoom-component.js',\n                        'src/plugins/timeline/time-axis-component.js',\n                        'src/plugins/timeline/cuepoints-component.js',\n                        'src/plugins/timeline/segments-component.js',\n                        'src/plugins/timeline/images-component.js',\n                        'src/plugins/timeline/histogram-component.js',\n                        'src/plugins/timeline/visual-component.js',\n                        'src/plugins/timeline/timeline.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-editor.min.js': [\n                        'src/plugins/editor/plugin-metadata-list-editor.js',\n                        'src/plugins/editor/plugin-metadata-block-editor.js',\n                        'src/plugins/editor/plugin-metadata-items-editor.js',\n                        'src/plugins/editor/plugin-metadata-item-editor.js',\n                        'src/plugins/editor/plugin-metadata-items-ref-editor.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-progress-bar.min.js': [\n                        'src/plugins/control-bar/progress-bar.js'\n                    ],\n                    'build/js/<%= pkg.name %>-plugin-storyboard.min.js': [\n                        'src/plugins/storyboard/base-frame-position-calculator.js',\n                        'src/plugins/storyboard/plugin-storyboard.js'\n                    ],\n                    'build/js/<%= pkg.name %>-yt-player.min.js': [\n                        'src/plugins/yt-player/yt-player.js'\n                    ],\n                    'build/js/<%= pkg.name %>-d3js-player.min.js': [\n                        'src/plugins/d3js-chart/plugin-d3js-chart.js'\n                    ]\n                }\n            }\n        },\n        sass: {\n            options: {\n                sourceMap: false\n            },\n            dist: {\n                files: {\n                    'build/css/<%= pkg.name %>.min.css': \"src/assets/sass/main.scss\"\n                }\n            }\n        },\n        webfont: {\n            icons: {\n                src: 'src/assets/icons/*.svg',\n                dest: 'src/assets/fonts',\n                destCss: 'src/assets/sass',\n                options: {\n                    font: 'ajs-webfont',\n                    stylesheet: 'scss',\n                    syntax: 'bem',\n                    htmlDemo: true,\n                    autoHint: true,\n                    hashes: true,\n                    relativeFontPath: '../fonts/',\n                    destHtml: 'samples',\n                    embed: true,\n                    engine: (grunt.option('engine') || 'fontforge'),\n                    templateOptions: {\n                        baseClass: 'ajs-icon',\n                        classPrefix: 'ajs-icon-',\n                        mixinPrefix: 'ajs-icon-'\n                    }\n                }\n            }\n        },\n        clean: {\n            build: [\n                'build',\n                'docs',\n                'tmp'\n            ]\n        },\n        copy: {\n            build: {\n                files: [\n                    {\n                        expand: true,\n                        cwd: 'src/assets/fonts',\n                        src: '**',\n                        dest: 'build/fonts',\n                        flatten: true,\n                        filter: 'isFile'\n                    }\n                ]\n            }\n        },\n        // qunit: {\n        //     build: [\n        //         'tests/**/*.html'\n        //     ]\n        // },\n        watch: {\n            uglify: {\n                files: [\n                    'src/**/*.js',\n                    'src/**/**/*.js'\n                ],\n                tasks: ['uglify:build']\n            },\n            sass: {\n                files: 'src/assets/sass/*.scss',\n                tasks: ['sass']\n            }\n\n        },\n        browserSync: {\n            dev: {\n                bsFiles: {\n                    src: [\n                        'build/css/*.css',\n                        'build/js/*.js'\n                    ]\n                },\n                options: {\n                    watchTask: true,\n                    proxy: \"localhost\"\n                }\n            }\n        }\n    });\n\n    //dev\n    grunt.registerTask('dev', [\n        'clean:build',\n        'webfont:icons',\n        'copy:build',\n        'uglify:build',\n        'sass:dist',\n        'browserSync',\n        'watch'\n    ]);\n\n    //Default\n    grunt.registerTask('default', [\n        'clean:build',\n        'jshint',\n        'qunit:build',\n        'uglify:build',\n        'sass:dist',\n        'copy:build'\n    ]);\n};"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "Amalia.js\n==========\n\nMetadata enriched HTML5 video player\n\nDownload and documentation are available here : http://ina-foss.github.io/amalia.js\n\n\n##Build project\nFor building the project execute the following lines:\n```sh\n$ bower install\n$ npm install\n$ grunt\n```\nThe amalia.js library is in the build directory. You can also have a look at some examples in the samples directory."
  },
  {
    "path": "bower.json",
    "content": "{\n    \"name\" : \"amalia.js\",\n    \"version\" : \"1.3.3\",\n    \"main\" : \"dist/js/amalia.js.min.js\",\n    \"license\" : \"MIT\",\n    \"ignore\" : [\n        \"**/.*\",\n        \"data\",\n        \"src\",\n        \"samples\",\n        \"tests\",\n        \"Gruntfile.js\",\n        \"package.json\"\n    ],\n    \"dependencies\" : {\n        \"jquery\" : \"~2.1.3\",\n        \"raphael\" : \"~2.1.3\",\n        \"jquery-ui\" : \"~1.11.2\"\n    },\n    \"devDependencies\":{\n        \"qunit\":\"1.14.0\"\n    },\n    \"keywords\" : [\n        \"javascript\",\n        \"library\",\n        \"annotation\",\n        \"metadata\",\n        \"amalia.js\",\n        \"player\",\n        \"html5\"\n    ]\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"amalia.js\",\n  \"description\": \"\",\n  \"homepage\": \"http://ina-foss.github.io/amalia.js\",\n  \"author\": \"INA\",\n  \"version\": \"1.3.3\",\n  \"public_site\": \"site\",\n  \"engines\": {\n    \"node\": \">= 0.11\"\n  },\n  \"license\": \"MIT\",\n  \"licenseText\": \"/**\\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\\n *\\n * This file is part of amalia.js\\n *\\n * Amalia.js is free software: you can redistribute it and/or modify it under\\n * the terms of the MIT License\\n *\\n * Redistributions of source code, javascript and css minified versions must\\n * retain the above copyright notice, this list of conditions and the following\\n * disclaimer\\n *\\n * Neither the name of the copyright holder nor the names of its contributors\\n * may be used to endorse or promote products derived from this software without\\n * specific prior written permission\\n *\\n * You should have received a copy of the MIT License along with\\n * amalia.js. If not, see <https://opensource.org/license/mit/>\\n *\\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\\n * A PARTICULAR PURPOSE.\\n */\\n\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/ina-foss/amalia.js.git\"\n  },\n  \"cacheDirectories\": [\n    \"node_modules\",\n    \"bower_components\"\n  ],\n  \"devDependencies\": {\n    \"browser-sync\": \"^2.11.1\",\n    \"grunt\": \"^0.4.5\",\n    \"grunt-browser-sync\": \"^2.2.0\",\n    \"grunt-contrib-clean\": \"^0.6.0\",\n    \"grunt-contrib-compress\": \"~0.13.0\",\n    \"grunt-contrib-copy\": \"^0.7.0\",\n    \"grunt-contrib-csslint\": \"^2.0.0\",\n    \"grunt-contrib-cssmin\": \"^5.0.0\",\n    \"grunt-contrib-jshint\": \"^0.11.3\",\n    \"grunt-contrib-less\": \"^3.0.0\",\n    \"grunt-contrib-qunit\": \"^0.5.2\",\n    \"grunt-contrib-uglify\": \"^0.7.0\",\n    \"grunt-contrib-watch\": \"^0.6.1\",\n    \"grunt-contrib-yuidoc\": \"^0.7.0\",\n    \"grunt-ftp-deploy\": \"^0.2.0\",\n    \"grunt-html-build\": \"~0.4.1\",\n    \"grunt-sass\": \"^1.1.0\",\n    \"grunt-webfont\": \"^0.5.1\",\n    \"load-grunt-tasks\": \"^3.5.0\",\n    \"time-grunt\": \"^1.3.0\"\n  },\n  \"dependencies\": {\n    \"bower\": \"^1.8.14\"\n  }\n}\n"
  },
  {
    "path": "releases.md",
    "content": "# Release notes\n## 1.3.3\n- License changed to MIT\n## 1.3.2\n - Fix ENDEN/ENDED\n - Change player html5 class with base class\n## 1.3.1\n - Add edition plugin\n - Add control bar with new widgets\n - New Documentation\n## 1.3.0\n - Delete dependencies bootstrap,font awesome ...\n - Add Metadata Java library\n## 1.2.3\n - Player with timeline, text-synchronization, caption and overlay plugins\n"
  },
  {
    "path": "samples/ajs-player.html",
    "content": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n    <meta http-equiv=\"Content-Language\" content=\"en-us\"/>\n    <link rel=\"icon\" href=\"static/img/favicon.ico\"/>\n    <meta name=\"description\"\n          content=\"amalia.js is a new extensible and versatile HTML5 multimedia player that allows you to view any type of metadata along with your video or audio streams\"/>\n    <meta name=\"keywords\"\n          content=\"amalia.js, ina, research, amalia, video, audio, multimedia, player, metadata, plugin, javascript, html5, timeline, keyframe, analysis, extraction, visualization, content, indexing, json, dataviz\"/>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n\n    <!-- This web site stuff -->\n    <script type=\"text/javascript\" src=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js\"></script>\n    <link href=\"http://maxcdn.bootstrapcdn.com/bootswatch/3.3.1/yeti/bootstrap.min.css\" rel=\"stylesheet\"/>\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"css/amalia-js-demo.css\"/>\n\n    <!-- amalia.js dependencies -->\n    <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n    <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>\n    <script src=\"../bower_components/raphael/raphael.js\"></script>\n\n    <!-- amalia.js player stuff -->\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../build/css/amalia.js.min.css\"/>\n    <script src=\"../build/js/amalia.js.min.js\"></script>\n    <!-- // -->\n    <!-- See at the end od this file for other Javascript stuff  -->\n    <title>amalia.js - Metadata enriched HTML5 video player</title>\n</head>\n<body>\n<div class=\"container\">\n    <!-- NAVBAR -->\n    <div class=\"navbar navbar-default navbar-fixed-top\" role=\"navigation\">\n        <div class=\"container\">\n            <div class=\"navbar-header\">\n                <a class=\"navbar-brand\" href=\"#\">amalia.js</a>\n            </div>\n            <div id=\"navbar\" class=\"collapse navbar-collapse\">\n                <ul class=\"nav navbar-nav pull-right\">\n                    <li><a class=\"scroll-to\" href=\"#download\">Download</a></li>\n                    <li><a class=\"scroll-to\" href=\"#documentation\">Documentation</a></li>\n                    <li><a class=\"scroll-to\" href=\"#examples\">Examples</a></li>\n                    <li><a class=\"scroll-to\" href=\"#about\">About</a></li>\n                    <li><a class=\"scroll-to\" href=\"#contact\">Contact</a></li>\n                </ul>\n            </div>\n        </div>\n    </div>\n    <div class=\"header\">\n        <h2 class=\"title\">Player</h2>\n\n        <p class=\"lead\"></p>\n    </div>\n    <div class=\"content\">\n        <div class=\"demo\">\n            <div style=\"height: 350px;\">\n                <div id=\"defaultPlayer\"></div>\n            </div>\n            <script>\n                $(function () {\n                    $(\"#defaultPlayer\").mediaPlayer({\n                        autoplay: false,\n                        src: \"../samples-data/examples/vid/amalia01.mp4\",\n                        controlBar: {\n                            autohide: false\n                        }\n                    });\n                });\n            </script>\n        </div>\n        <div class=\"doc\">\n            <h1>Player options</h1>\n            <!-- Player Options-->\n            <ul class=\"nav nav-tabs\">\n                <li class=\"active\"><a href=\"#player-options\" data-toggle=\"tab\" aria-expanded=\"false\">Doc</a></li>\n                <li class=\"\"><a href=\"#player-options-example\" data-toggle=\"tab\" aria-expanded=\"true\">example</a></li>\n            </ul>\n            <div class=\"tab-content\">\n                <div id=\"player-options\" class=\"panel panel-default\">\n                    <div class=\"panel-body\">\n                        <dl class=\"dl-horizontal\">\n                            <dt>src</dt>\n                            <dd>URL to a video or audio file</dd>\n\n                            <dt>poster</dt>\n                            <dd>URL to a poster image to display before playback starts</dd>\n\n                            <dt>autoplay</dt>\n                            <dd>boolean, automatically start playing the video on page load</dd>\n\n                            <dt>defaultVolume</dt>\n                            <dd>75 by default. This value is overloaded by the volume set by the user which is kept in a\n                                cookie\n                            </dd>\n\n                            <dt>controlBar</dt>\n                            <dd>parameters for the control buttons (see below)</dd>\n\n                            <dt>plugins</dt>\n                            <dd>plugins configuration list (see below)</dd>\n\n                            <dt>framerate</dt>\n                            <dd></dd>\n\n                            <dt>controlBarClassName</dt>\n                            <dd></dd>\n\n                            <dt>controlBar</dt>\n                            <dd></dd>\n\n                            <dt>tcOffset</dt>\n                            <dd></dd>\n\n                            <dt>mediaType</dt>\n                            <dd></dd>\n\n                            <dt>thumbRootDirectory</dt>\n                            <dd></dd>\n\n                            <dt>crossorigin</dt>\n                            <dd></dd>\n\n                            <dt>shortcuts</dt>\n                            <dd></dd>\n                        </dl>\n                    </div>\n                </div>\n                <div id=\"player-options-example\">\n                <pre>\n$(\"#container\").mediaPlayer({\n    autoplay: true,\n    src: \"http://www-player-i.ina.fr/medias/demo/audi.mp4\"\n});\n                </pre>\n                </div>\n            </div>\n            <!-- END: Player Options-->\n\n            <div class=\"control-bar panel panel-default\">\n                <div class=\"panel-heading\">\n                    <h3 class=\"panel-title\">Control bar options</h3>\n                </div>\n                <div class=\"panel-body\">\n                    <dl class=\"dl-horizontal\">\n                        <dt>...</dt>\n                        <dd>...</dd>\n                    </dl>\n                </div>\n            </div>\n            <div class=\"plugin panel panel-default\">\n                <div class=\"panel-heading\">\n                    <h3 class=\"panel-title\">Plugin options</h3>\n                </div>\n                <div class=\"panel-body\">\n                    <dl class=\"dl-horizontal\">\n                        <dt>...</dt>\n                        <dd>...</dd>\n                    </dl>\n                </div>\n            </div>\n        </div>\n\n    </div>\n</div>\n<!-- FOOTER -->\n<div class=\"page-footer  breadcrumb\">\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col-md-6\">Last updated : <%= updateDate %></div>\n            <div class=\"col-md-6 text-right\">\n                <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\"\n                                                                                   src=\"img/logo_ina.jpg\"\n                                                                                   style=\" margin-right:10px; width: 32x; height: 32px\"/>\n                    Institut National de l'Audiovisuel - INA</a> ©<%= updateYear %>\n            </div>\n        </div>\n    </div>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "samples/ajs-plugin-captions.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Amalia.js</title>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"icon\" href=\"images/favicon.ico\">\n        <link href=\"css/default.css\" rel=\"stylesheet\">\n        <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n        <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>        \n        <script src=\"../bower_components/raphael/raphael.js\"></script>\n        <!-- style-player -->        \n        <link href=\"../build/css/amalia.js.min.css\" rel=\"stylesheet\">\n        <!-- /style-player -->        \n        <!-- script-player -->        \n        <script src=\"../build/js/amalia.js.min.js\"></script>\n        <script src=\"../build/js/amalia.js-plugin-captions.min.js\"></script>        \n        <!-- /script-player -->\n    </head>\n    <body>\n        <div class=\"container\">\n            <div class=\"header\">\n                <h1>Samples</h1>  \n            </div>\n            <div class=\"content\">\n                <div class=\"demo\">\n                    <div style=\"height: 350px;\">\n                        <div id=\"defaultPlayer\"></div>\n                    </div>\n                    <script>\n                        $( function () {\n                            $( \"#defaultPlayer\" ).mediaPlayer( {\n                                autoplay : false,\n                                src : \"../samples-data/examples/vid/amalia01.mp4\",\n                                controlBar :\n                                    {\n                                        sticky : true\n                                    },\n                                plugins : {\n                                    dataServices :\n                                        [\n                                            '../samples-data/examples/json/amalia01-text.json'\n                                        ],\n                                    list :\n                                        [\n                                            {\n                                                'className' : 'fr.ina.amalia.player.plugins.CaptionsPlugin',\n                                                'parameters' : {\n                                                    metadataId : 'text-amalia01',\n                                                    level : 1\n                                                }\n                                            }\n                                        ]\n                                }\n                            } );\n                        } );\n                    </script>\n                    <div>\n                        <pre class=\"config\">\n                        dataServices :\n                            [\n                                '../samples-data/examples/json/amalia01-text.json'\n                            ],\n                        list :\n                            [\n                                {\n                                    'className' : 'fr.ina.amalia.player.plugins.CaptionsPlugin',\n                                    'parameters' : {\n                                        metadataId : 'text-amalia01',\n                                        level : 1\n                                    }\n                                }\n                            ]                      \n                        </pre>\n                    </div>\n                </div>\n            </div>\n            <div class=\"footer\">\n                <p>\n                    <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\" src=\"images/logo_ina.jpg\" style=\" margin-right:10px; width: 32x; height: 32px\" />Institut National de l'Audiovisuel - INA</a>© 2015\n                </p>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "samples/ajs-plugin-editor.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Amalia.js</title>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"icon\" href=\"images/favicon.ico\">\n        <link href=\"css/default.css\" rel=\"stylesheet\">\n        <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n        <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>        \n        <script src=\"../bower_components/raphael/raphael.js\"></script>\n        <!-- style-player -->        \n        <link href=\"../build/css/amalia.js.min.css\" rel=\"stylesheet\">\n        <!-- /style-player -->        \n        <!-- script-player -->        \n        <script src=\"../build/js/amalia.js.min.js\"></script>\n        <script src=\"../build/js/amalia.js-plugin-timeline.min.js\"></script>    \n        <script src=\"../build/js/amalia.js-plugin-editor.min.js\"></script>    \n        <!-- /script-player -->\n    </head>\n    <body>\n        <div class=\"container\">\n            <div class=\"header\">\n                <h1>Text synchronization plugin</h1>  \n            </div>\n            <div class=\"content\">           \n                <div class=\"demo\">\n                    <div style=\" clear: both;\">\n\n                        <div style=\"width: 70%;float: left;\">\n                            <div style=\"height: 350px;\">\n                                <div id=\"defaultPlayer\"></div>\n                            </div> \n                            <div id=\"player_timelinePlugin\" style=\"height: 500px;\"></div>\n                        </div>\n                        <div style=\"width: 30%; float: left;\">\n                            <div id='player-list-editor-plugin'></div>\n                            <br />\n                            <div id='player-block-editor-plugin'></div>\n                            <br />\n                            <div id='player-items-editor-plugin'></div>\n                        </div>\n                    </div>\n\n                    <script>\n                        $( function () {\n                            $( \"#defaultPlayer\" ).mediaPlayer( {\n                                autoplay : false,\n                                src : \"../samples-data/examples/vid/amalia01.mp4\",\n                                controlBar :\n                                    {\n                                        sticky : true\n                                    },\n                                plugins :\n                                    {\n                                        dataServices : [\n                                            '../samples-data/examples/json/amalia01-events.json',\n                                            '../samples-data/examples/json/amalia01-kf.json',\n                                            '../samples-data/examples/json/amalia01-ball.json'\n                                        ],\n                                        list : [\n                                            {\n                                                'className' : 'fr.ina.amalia.player.plugins.MetadataListEditorPlugin',\n                                                'container' : '#player-list-editor-plugin',\n                                                'debug' : true,\n                                                'parameters' : {\n                                                    defaultDataType : fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION,\n                                                    defaultAuthor : 'Amalia.js',\n                                                    defaultColor : '#2196f3',\n                                                    defaultShape : 'car'\n                                                }\n                                            },\n                                            {\n                                                'className' : 'fr.ina.amalia.player.plugins.MetadataBlockEditorPlugin',\n                                                'container' : '#player-block-editor-plugin',\n                                                'debug' : true,\n                                                'parameters' : {\n                                                }\n                                            },\n                                            {\n                                                'className' : 'fr.ina.amalia.player.plugins.MetadataItemsEditorPlugin',\n                                                'container' : '#player-items-editor-plugin',\n                                                'debug' : true,\n                                                'parameters' : {\n                                                }\n                                            },\n                                            {\n                                                'className' : 'fr.ina.amalia.player.plugins.TimelinePlugin',\n                                                'container' : '#player_timelinePlugin',\n                                                'debug' : false,\n                                                'parameters' : {\n                                                    editingMode : true,\n                                                    displayLines : 3,\n                                                    resizable : true,\n                                                    lineDisplayMode : fr.ina.amalia.player.plugins.PluginBaseMultiBlocks.METADATA_DISPLAY_TYPE.STATIC_DYNAMIC,//STATIC//STATIC_DYNAMIC//DYNAMIC                           \n                                                    listOfLines : [\n                                                        {\n                                                            title : 'Events',\n                                                            type : 'cuepoint',\n                                                            metadataId : 'events-amalia01',\n                                                            color : '#3CF',\n                                                            pointNav : true\n                                                        },\n                                                        {\n                                                            title : 'Ball moving up',\n                                                            type : 'segment',\n                                                            metadataId : 'ball-amalia01',\n                                                            color : '#F00'\n                                                        }\n                                                    ]\n                                                }\n                                            }\n                                        ]\n\n                                    }\n                            } );\n                        } );\n                    </script>\n\n                </div>\n            </div>\n            <div class=\"footer\">\n                <p>\n                    <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\" src=\"images/logo_ina.jpg\" style=\" margin-right:10px; width: 32x; height: 32px\" />Institut National de l'Audiovisuel - INA</a>© 2015\n                </p>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "samples/ajs-plugin-overlay.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Amalia.js</title>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"icon\" href=\"images/favicon.ico\">\n        <link href=\"css/default.css\" rel=\"stylesheet\">\n        <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n        <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>        \n        <script src=\"../bower_components/raphael/raphael.js\"></script>\n        <!-- style-player -->        \n        <link href=\"../build/css/amalia.js.min.css\" rel=\"stylesheet\">\n        <!-- /style-player -->        \n        <!-- script-player -->        \n        <script src=\"../build/js/amalia.js.min.js\"></script>\n        <script src=\"../build/js/amalia.js-plugin-overlay.min.js\"></script>\n        <!-- /script-player -->\n    </head>\n    <body>\n        <div class=\"container\">\n            <div class=\"header\">\n                <h1>Plugin Overlay</h1>  \n            </div>\n            <div class=\"content\">           \n                <div class=\"demo\">\n                    <div style=\"height: 350px;\">\n                        <div id=\"defaultPlayer\"></div>\n                    </div>\n                    <script>\n                        $( function () {\n                            $( \"#defaultPlayer\" ).mediaPlayer( {\n                                autoplay : false,\n                                src : \"../samples-data/examples/vid/amalia01.mp4\",\n                                plugins : {\n                                    dataServices : [\n                                        {\n\t\t                                    protocol : \"http\",\n        \t\t                            url : '../samples-data/examples/json/amalia01-overlay.json',\n                \t\t                    // format : 'json',\n                        \t\t            parameters : {\n                                \t\t    \tmainLevel : true\n                                    \t\t},\n                                \t\t}\n                                    ],\n                                    list : [\n                                        {\n                                            'className' : 'fr.ina.amalia.player.plugins.OverlayPlugin',\n//                                             'metadataId' : 'tracks-amalia01-1',\n                                            'parameters' : {\n                                            \teditable:false,\n                                                style : {\n                                                    'fill' : '#000',\n                                                    'strokeWidth' : 1,\n                                                    'stroke' : '#000',\n                                                    'fillOpacity' : 0,\n                                                    'strokeDasharray' : '- '\n                                                }\n                                            }\n                                        }\n                                    ]\n                                }\n                            } );\n                        } );\n                    </script>\n                    <div>\n                        <pre class=\"config\">\n                        {\n                            'className' : 'fr.ina.amalia.player.plugins.OverlayPlugin',\n                            'metadataId' : 'tracks-amalia01-1',\n                            'parameters' : {\n                                style : {\n                                    'fill' : '#000',\n                                    'strokeWidth' : 1,\n                                    'stroke' : '#000',\n                                    'fillOpacity' : 0,\n                                    'strokeDasharray' : '- '\n                                }\n                            }\n                        }                 \n                        </pre>\n                    </div>\n                </div>\n            </div>\n            <div class=\"footer\">\n                <p>\n                    <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\" src=\"images/logo_ina.jpg\" style=\" margin-right:10px; width: 32x; height: 32px\" />Institut National de l'Audiovisuel - INA</a>© 2015\n                </p>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "samples/ajs-plugin-text-sync.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Amalia.js</title>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"icon\" href=\"images/favicon.ico\">\n        <link href=\"css/default.css\" rel=\"stylesheet\">\n        <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n        <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>        \n        <script src=\"../bower_components/raphael/raphael.js\"></script>\n        <!-- style-player -->        \n        <link href=\"../build/css/amalia.js.min.css\" rel=\"stylesheet\">\n        <!-- /style-player -->        \n        <!-- script-player -->        \n        <script src=\"../build/js/amalia.js.min.js\"></script>\n        <script src=\"../build/js/amalia.js-plugin-text-sync.min.js\"></script>        \n        <!-- /script-player -->\n    </head>\n    <body>\n        <div class=\"container\">\n            <div class=\"header\">\n                <h1>Text synchronization plugin</h1>  \n            </div>\n            <div class=\"content\">           \n                <div class=\"demo\">\n                    <div style=\" clear: both;\">\n\n                        <div style=\"width: 50%;float: left;\">\n                            <div style=\"height: 350px;\">\n                                <div id=\"defaultPlayer\"></div>\n                            </div>\n                            <div>\n                                <pre class=\"config\">\n{\n    'className' : 'fr.ina.amalia.player.plugins.TextSyncPlugin',\n    'container' : '#myplayer-tsync-tsync',\n    'parameters' : {\n        metadataId : 'text-amalia01',\n        title : 'My title',\n        description : 'A description I may have to put here',\n        level : 1,\n        displayLevel : 1,\n        scrollAuto : true\n    }\n}                 \n                                </pre>\n                            </div>\n                        </div>\n                        <div style=\"width: 50%; float: left;\">\n                            <div id=\"text_sync_plugin\" style=\"height: 500px;\"></div>\n                        </div>\n                    </div>\n\n                    <script>\n                        $( function () {\n                            $( \"#defaultPlayer\" ).mediaPlayer( {\n                                autoplay : false,\n                                src : \"../samples-data/examples/vid/amalia01.mp4\",\n                                controlBar :\n                                    {\n                                        sticky : true\n                                    },\n                                plugins : {\n                                    dataServices : [\n                                        '../samples-data/examples/json/amalia01-text.json'\n                                    ],\n                                    list : [\n                                        {\n                                            'className' : 'fr.ina.amalia.player.plugins.TextSyncPlugin',\n                                            'container' : '#text_sync_plugin',\n                                            'parameters' : {\n                                                metadataId : 'text-amalia01',\n                                                title : 'My title',\n                                                description : 'A description I may have to put here',\n                                                level : 1,\n                                                displayLevel : 1,\n                                                scrollAuto : true\n                                            }\n                                        }\n                                    ]\n                                }\n                            } );\n                        } );\n                    </script>\n\n                </div>\n            </div>\n            <div class=\"footer\">\n                <p>\n                    <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\" src=\"images/logo_ina.jpg\" style=\" margin-right:10px; width: 32x; height: 32px\" />Institut National de l'Audiovisuel - INA</a>© 2015\n                </p>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "samples/ajs-plugin-timeline.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Amalia.js</title>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"icon\" href=\"images/favicon.ico\">\n        <link href=\"css/default.css\" rel=\"stylesheet\">\n        <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n        <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>        \n        <script src=\"../bower_components/raphael/raphael.js\"></script>\n        <!-- style-player -->        \n        <link href=\"../build/css/amalia.js.min.css\" rel=\"stylesheet\">\n        <!-- /style-player -->        \n        <!-- script-player -->        \n        <script src=\"../build/js/amalia.js.min.js\"></script>\n        <script src=\"../build/js/amalia.js-plugin-timeline.min.js\"></script>        \n        <!-- /script-player -->\n    </head>\n    <body>\n        <div class=\"container\">\n            <div class=\"header\">\n                <h1>Plugin timeline</h1>  \n            </div>\n            <div class=\"content\">           \n                <div class=\"demo\">\n                    <div style=\"height: 350px;\">\n                        <div id=\"defaultPlayer\"></div>\n                    </div>\n                    <div id=\"timeline\"></div>\n                    <script>\n                        $( function () {\n                            $( \"#defaultPlayer\" ).mediaPlayer( {\n                                autoplay : false,\n                                src : \"../samples-data/examples/vid/amalia01.mp4\",\n                                controlBar :\n                                    {\n                                        sticky : true\n                                    },\n                                thumbRootDirectory:'../',\n                                plugins : {\n                                    dataServices : [\n                                        '../samples-data/examples/json/amalia01-events.json',\n                                        '../samples-data/examples/json/amalia01-kf.json',\n                                        '../samples-data/examples/json/amalia01-ball.json'\n                                    ],\n                                    list : [\n                                        {\n                                            'className' : 'fr.ina.amalia.player.plugins.TimelinePlugin',\n                                            'container' : '#timeline',\n                                            'parameters' : {\n                                                listOfLines : [\n                                                    {\n                                                        title : 'Events',\n                                                        type : 'cuepoint',\n                                                        metadataId : 'events-amalia01',\n                                                        color : '#3CF',\n                                                        pointNav : true\n                                                    },\n                                                    {\n                                                        title : 'Ball moving up',\n                                                        type : 'segment',\n                                                        metadataId : 'ball-amalia01',\n                                                        color : '#F00'\n                                                    },\n                                                    {\n                                                        title : 'Keyframes every 2s',\n                                                        type : 'image',\n                                                        metadataId : 'kf-amalia01',\n                                                        pointNav : true\n                                                    }\n                                                ]\n                                            }\n                                        }\n                                    ]\n                                }\n                            } );\n                        } );\n                    </script>\n                    <div>\n                        <pre class=\"config\">\n                        {\n                            'className' : 'fr.ina.amalia.player.plugins.WatermarkPlugin',                                                \n                            'parameters' :\n                                {\n                                    backgroundImageUrl : 'images/logo_ina.jpg',\n                                    ratio : 50,\n                                    placement : 'c',\n                                    imageWidth : 300,\n                                    imageHeight : 298\n                                }\n                        }                     \n                        </pre>\n                    </div>\n                </div>\n            </div>\n            <div class=\"footer\">\n                <p>\n                    <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\" src=\"images/logo_ina.jpg\" style=\" margin-right:10px; width: 32x; height: 32px\" />Institut National de l'Audiovisuel - INA</a>© 2015\n                </p>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "samples/ajs-plugin-watermark.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <title>Amalia.js</title>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <link rel=\"icon\" href=\"images/favicon.ico\">\n        <link href=\"css/default.css\" rel=\"stylesheet\">\n        <script src=\"../bower_components/jquery/dist/jquery.js\"></script>\n        <script src=\"../bower_components/jquery-ui/jquery-ui.min.js\"></script>        \n        <script src=\"../bower_components/raphael/raphael.js\"></script>\n        <!-- style-player -->        \n        <link href=\"../build/css/amalia.js.min.css\" rel=\"stylesheet\">\n        <!-- /style-player -->        \n        <!-- script-player -->        \n        <script src=\"../build/js/amalia.js.min.js\"></script>\n        <script src=\"../build/js/amalia.js-plugin-watermark.min.js\"></script>        \n        <!-- /script-player -->\n    </head>\n    <body>\n        <div class=\"container\">\n            <div class=\"header\">\n                <h1>Samples</h1>  \n            </div>\n            <div class=\"content\">           \n                <div class=\"demo\">\n                    <div style=\"height: 350px;\">\n                        <div id=\"defaultPlayer\"></div>\n                    </div>\n                    <script>\n                        $( function () {\n                            $( \"#defaultPlayer\" ).mediaPlayer( {\n                                autoplay : false,\n                                src : \"../samples-data/examples/vid/amalia01.mp4\",\n                                controlBar :\n                                    {\n                                        sticky : true\n                                    },\n                                plugins : {\n                                    dataServices :\n                                        [\n                                        ],\n                                    list :\n                                        [\n                                            {\n                                                'className' : 'fr.ina.amalia.player.plugins.WatermarkPlugin',\n                                                'parameters' :\n                                                    {\n                                                        backgroundImageUrl : 'images/logo_ina.jpg',\n                                                        ratio : 50,\n                                                        placement : 'c',\n                                                        imageWidth : 300,\n                                                        imageHeight : 298\n                                                    }\n                                            }\n                                        ]\n                                }\n                            } );\n                        } );\n                    </script>\n                    <div>\n                        <pre class=\"config\">\n                        {\n                            'className' : 'fr.ina.amalia.player.plugins.WatermarkPlugin',                                                \n                            'parameters' :\n                                {\n                                    backgroundImageUrl : 'images/logo_ina.jpg',\n                                    ratio : 50,\n                                    placement : 'c',\n                                    imageWidth : 300,\n                                    imageHeight : 298\n                                }\n                        }                     \n                        </pre>\n                    </div>\n                </div>\n            </div>\n            <div class=\"footer\">\n                <p>\n                    <a href=\"http://www.institut-national-audiovisuel.fr/en/home\"><img class=\"logo-ina\" src=\"images/logo_ina.jpg\" style=\" margin-right:10px; width: 32x; height: 32px\" />Institut National de l'Audiovisuel - INA</a>© 2015\n                </p>\n            </div>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "samples/ajs-webfont.html",
    "content": "<!doctype html>\n<html>\n\t<head>\n\t\t<meta charset=\"utf-8\">\n\t\t<title>ajs-webfont</title>\n\t\t<style>\n\t\tbody {\n\t\t\tmargin:0;\n\t\t\tpadding:10px 20px;\n\t\t\tbackground:#fff;\n\t\t\tcolor:#222;\n\t\t\t}\n\t\th1, div, footer {\n\t\t\tfont-family:\"Helvetica Neue\", Arial, sans-serif;\n\t\t\t}\n\t\th1 {\n\t\t\tmargin:0 0 20px;\n\t\t\tfont-size:32px;\n\t\t\tfont-weight:normal;\n\t\t\t}\n\t\t.icons {\n\t\t\tmargin-bottom:40px;\n\t\t\t-webkit-column-count:5;\n\t\t\t   -moz-column-count:5;\n\t\t\t        column-count:5;\n\t\t\t-webkit-column-gap:20px;\n\t\t\t   -moz-column-gap:20px;\n\t\t\t        column-gap:20px;\n\t\t\t}\n\t\t.icons__item,\n\t\t.icons__item i {\n\t\t\tline-height:2em;\n\t\t\tcursor:pointer;\n\t\t\toverflow:hidden;\n\t\t\t}\n\t\t.icons__item:hover {\n\t\t\tcolor:#3c90be;\n\t\t\t}\n\t\t.icons__item i {\n\t\t\tdisplay:inline-block;\n\t\t\twidth:32px;\n\t\t\ttext-align:center;\n\t\t\t}\n\t\t.icons__item:hover i {\n\t\t\t-webkit-transform:scale(1.5);\n\t\t\t        transform:scale(1.5);\n\t\t\t}\n\t\tfooter {\n\t\t\tmargin-top:40px;\n\t\t\tfont-size:14px;\n\t\t\tcolor:#999;\n\t\t\t}\n\n\t\t/* Generated by grunt-webfont */\n\n\n\n@font-face {\n\tfont-family:\"ajs-webfont\";\n\tsrc:url(\"../src/assets/fonts/ajs-webfont.eot?da2b33fb7481b6e5ffca9457ecddfd8e\");\n\tfont-weight:normal;\n\tfont-style:normal;\n}\n@font-face {\n\tfont-family:\"ajs-webfont\";\n\tsrc:url(\"../src/assets/fonts/ajs-webfont.eot?da2b33fb7481b6e5ffca9457ecddfd8e\");\n\tsrc:url(\"../src/assets/fonts/ajs-webfont.eot?#iefix\") format(\"embedded-opentype\"),\n\t\turl(\"data:application/x-font-woff;charset=utf-8;base64,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\") format(\"woff\"),\n\t\turl(\"../src/assets/fonts/ajs-webfont.ttf?da2b33fb7481b6e5ffca9457ecddfd8e\") format(\"truetype\");\n\tfont-weight:normal;\n\tfont-style:normal;\n}\n\n.ajs-icon {\n\tfont-family:\"ajs-webfont\";\n\tdisplay:inline-block;\n\tvertical-align:middle;\n\tline-height:1;\n\tfont-weight:normal;\n\tfont-style:normal;\n\tspeak:none;\n\ttext-decoration:inherit;\n\ttext-transform:none;\n\ttext-rendering:auto;\n\t-webkit-font-smoothing:antialiased;\n\t-moz-osx-font-smoothing:grayscale;\n}\n\n\n/* Icons */\n\n\n.ajs-icon-account:before {\n\tcontent:\"\\f101\";\n}\n\n\n.ajs-icon-add-whole-segment:before {\n\tcontent:\"\\f102\";\n}\n\n\n.ajs-icon-amalia-js:before {\n\tcontent:\"\\f103\";\n}\n\n\n.ajs-icon-arrows-h:before {\n\tcontent:\"\\f104\";\n}\n\n\n.ajs-icon-arrows-v:before {\n\tcontent:\"\\f105\";\n}\n\n\n.ajs-icon-bell:before {\n\tcontent:\"\\f106\";\n}\n\n\n.ajs-icon-building:before {\n\tcontent:\"\\f107\";\n}\n\n\n.ajs-icon-caret-right:before {\n\tcontent:\"\\f108\";\n}\n\n\n.ajs-icon-cercle:before {\n\tcontent:\"\\f109\";\n}\n\n\n.ajs-icon-check:before {\n\tcontent:\"\\f10a\";\n}\n\n\n.ajs-icon-chevron-circle-down:before {\n\tcontent:\"\\f10b\";\n}\n\n\n.ajs-icon-chevron-circle-left:before {\n\tcontent:\"\\f10c\";\n}\n\n\n.ajs-icon-chevron-circle-right:before {\n\tcontent:\"\\f10d\";\n}\n\n\n.ajs-icon-chevron-circle-up:before {\n\tcontent:\"\\f10e\";\n}\n\n\n.ajs-icon-chevron-down:before {\n\tcontent:\"\\f10f\";\n}\n\n\n.ajs-icon-chevron-left:before {\n\tcontent:\"\\f110\";\n}\n\n\n.ajs-icon-chevron-right:before {\n\tcontent:\"\\f111\";\n}\n\n\n.ajs-icon-chevron-up:before {\n\tcontent:\"\\f112\";\n}\n\n\n.ajs-icon-chromecast-active:before {\n\tcontent:\"\\f113\";\n}\n\n\n.ajs-icon-chromecast:before {\n\tcontent:\"\\f114\";\n}\n\n\n.ajs-icon-circle:before {\n\tcontent:\"\\f115\";\n}\n\n\n.ajs-icon-cog:before {\n\tcontent:\"\\f116\";\n}\n\n\n.ajs-icon-cogs:before {\n\tcontent:\"\\f117\";\n}\n\n\n.ajs-icon-comment:before {\n\tcontent:\"\\f118\";\n}\n\n\n.ajs-icon-compress:before {\n\tcontent:\"\\f119\";\n}\n\n\n.ajs-icon-control-backward:before {\n\tcontent:\"\\f11a\";\n}\n\n\n.ajs-icon-control-fast-forward:before {\n\tcontent:\"\\f11b\";\n}\n\n\n.ajs-icon-control-fast-rewind:before {\n\tcontent:\"\\f11c\";\n}\n\n\n.ajs-icon-control-forward:before {\n\tcontent:\"\\f11d\";\n}\n\n\n.ajs-icon-control-pause:before {\n\tcontent:\"\\f11e\";\n}\n\n\n.ajs-icon-control-play:before {\n\tcontent:\"\\f11f\";\n}\n\n\n.ajs-icon-control-rewind:before {\n\tcontent:\"\\f120\";\n}\n\n\n.ajs-icon-controlbar-compress:before {\n\tcontent:\"\\f121\";\n}\n\n\n.ajs-icon-controlbar-fullscreen:before {\n\tcontent:\"\\f122\";\n}\n\n\n.ajs-icon-controlbar-pause:before {\n\tcontent:\"\\f123\";\n}\n\n\n.ajs-icon-controlbar-play:before {\n\tcontent:\"\\f124\";\n}\n\n\n.ajs-icon-controlbar-settings:before {\n\tcontent:\"\\f125\";\n}\n\n\n.ajs-icon-controlbar-volume-left-off:before {\n\tcontent:\"\\f126\";\n}\n\n\n.ajs-icon-controlbar-volume-left:before {\n\tcontent:\"\\f127\";\n}\n\n\n.ajs-icon-controlbar-volume-min:before {\n\tcontent:\"\\f128\";\n}\n\n\n.ajs-icon-controlbar-volume-off:before {\n\tcontent:\"\\f129\";\n}\n\n\n.ajs-icon-controlbar-volume-right-off:before {\n\tcontent:\"\\f12a\";\n}\n\n\n.ajs-icon-controlbar-volume-right:before {\n\tcontent:\"\\f12b\";\n}\n\n\n.ajs-icon-controlbar-volume_max:before {\n\tcontent:\"\\f12c\";\n}\n\n\n.ajs-icon-controlbar-volume_middle:before {\n\tcontent:\"\\f12d\";\n}\n\n\n.ajs-icon-download:before {\n\tcontent:\"\\f12e\";\n}\n\n\n.ajs-icon-eject:before {\n\tcontent:\"\\f12f\";\n}\n\n\n.ajs-icon-ellipsis-h:before {\n\tcontent:\"\\f130\";\n}\n\n\n.ajs-icon-ellipsis-v:before {\n\tcontent:\"\\f131\";\n}\n\n\n.ajs-icon-eraser:before {\n\tcontent:\"\\f132\";\n}\n\n\n.ajs-icon-expand:before {\n\tcontent:\"\\f133\";\n}\n\n\n.ajs-icon-eye-off:before {\n\tcontent:\"\\f134\";\n}\n\n\n.ajs-icon-eye-on:before {\n\tcontent:\"\\f135\";\n}\n\n\n.ajs-icon-facetime:before {\n\tcontent:\"\\f136\";\n}\n\n\n.ajs-icon-female:before {\n\tcontent:\"\\f137\";\n}\n\n\n.ajs-icon-github:before {\n\tcontent:\"\\f138\";\n}\n\n\n.ajs-icon-information:before {\n\tcontent:\"\\f139\";\n}\n\n\n.ajs-icon-jogs-backward-0x:before {\n\tcontent:\"\\f13a\";\n}\n\n\n.ajs-icon-jogs-backward-1x:before {\n\tcontent:\"\\f13b\";\n}\n\n\n.ajs-icon-jogs-backward-2x:before {\n\tcontent:\"\\f13c\";\n}\n\n\n.ajs-icon-jogs-backward-3x:before {\n\tcontent:\"\\f13d\";\n}\n\n\n.ajs-icon-jogs-backward-4x:before {\n\tcontent:\"\\f13e\";\n}\n\n\n.ajs-icon-jogs-center:before {\n\tcontent:\"\\f13f\";\n}\n\n\n.ajs-icon-jogs-fast-backward:before {\n\tcontent:\"\\f140\";\n}\n\n\n.ajs-icon-jogs-fast-forward:before {\n\tcontent:\"\\f141\";\n}\n\n\n.ajs-icon-jogs-forward-0x:before {\n\tcontent:\"\\f142\";\n}\n\n\n.ajs-icon-jogs-forward-1x:before {\n\tcontent:\"\\f143\";\n}\n\n\n.ajs-icon-jogs-forward-2x:before {\n\tcontent:\"\\f144\";\n}\n\n\n.ajs-icon-jogs-forward-3x:before {\n\tcontent:\"\\f145\";\n}\n\n\n.ajs-icon-jogs-forward-4x:before {\n\tcontent:\"\\f146\";\n}\n\n\n.ajs-icon-key:before {\n\tcontent:\"\\f147\";\n}\n\n\n.ajs-icon-legal:before {\n\tcontent:\"\\f148\";\n}\n\n\n.ajs-icon-list-close:before {\n\tcontent:\"\\f149\";\n}\n\n\n.ajs-icon-list-open:before {\n\tcontent:\"\\f14a\";\n}\n\n\n.ajs-icon-lock-close:before {\n\tcontent:\"\\f14b\";\n}\n\n\n.ajs-icon-lock-open:before {\n\tcontent:\"\\f14c\";\n}\n\n\n.ajs-icon-male:before {\n\tcontent:\"\\f14d\";\n}\n\n\n.ajs-icon-microphone-off:before {\n\tcontent:\"\\f14e\";\n}\n\n\n.ajs-icon-microphone-on:before {\n\tcontent:\"\\f14f\";\n}\n\n\n.ajs-icon-minus:before {\n\tcontent:\"\\f150\";\n}\n\n\n.ajs-icon-music:before {\n\tcontent:\"\\f151\";\n}\n\n\n.ajs-icon-picture:before {\n\tcontent:\"\\f152\";\n}\n\n\n.ajs-icon-plus:before {\n\tcontent:\"\\f153\";\n}\n\n\n.ajs-icon-power:before {\n\tcontent:\"\\f154\";\n}\n\n\n.ajs-icon-refresh:before {\n\tcontent:\"\\f155\";\n}\n\n\n.ajs-icon-remove:before {\n\tcontent:\"\\f156\";\n}\n\n\n.ajs-icon-reorder:before {\n\tcontent:\"\\f157\";\n}\n\n\n.ajs-icon-screenshot:before {\n\tcontent:\"\\f158\";\n}\n\n\n.ajs-icon-scrubber-cursor:before {\n\tcontent:\"\\f159\";\n}\n\n\n.ajs-icon-search:before {\n\tcontent:\"\\f15a\";\n}\n\n\n.ajs-icon-sign-in:before {\n\tcontent:\"\\f15b\";\n}\n\n\n.ajs-icon-sign-out:before {\n\tcontent:\"\\f15c\";\n}\n\n\n.ajs-icon-sort:before {\n\tcontent:\"\\f15d\";\n}\n\n\n.ajs-icon-sound-link-off:before {\n\tcontent:\"\\f15e\";\n}\n\n\n.ajs-icon-sound-link-on:before {\n\tcontent:\"\\f15f\";\n}\n\n\n.ajs-icon-stop:before {\n\tcontent:\"\\f160\";\n}\n\n\n.ajs-icon-transcription:before {\n\tcontent:\"\\f161\";\n}\n\n\n.ajs-icon-volume-down:before {\n\tcontent:\"\\f162\";\n}\n\n\n.ajs-icon-volume-off:before {\n\tcontent:\"\\f163\";\n}\n\n\n.ajs-icon-volume-up:before {\n\tcontent:\"\\f164\";\n}\n\n\n.ajs-icon-zoom-in:before {\n\tcontent:\"\\f165\";\n}\n\n\n.ajs-icon-zoom-out:before {\n\tcontent:\"\\f166\";\n}\n\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<h1>ajs-webfont</h1>\n\n\t\t<div class=\"icons\" id=\"icons\">\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"account\"><i class=\"ajs-icon ajs-icon-account\"></i> ajs-icon-account</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"add-whole-segment\"><i class=\"ajs-icon ajs-icon-add-whole-segment\"></i> ajs-icon-add-whole-segment</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"amalia-js\"><i class=\"ajs-icon ajs-icon-amalia-js\"></i> ajs-icon-amalia-js</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"arrows-h\"><i class=\"ajs-icon ajs-icon-arrows-h\"></i> ajs-icon-arrows-h</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"arrows-v\"><i class=\"ajs-icon ajs-icon-arrows-v\"></i> ajs-icon-arrows-v</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"bell\"><i class=\"ajs-icon ajs-icon-bell\"></i> ajs-icon-bell</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"building\"><i class=\"ajs-icon ajs-icon-building\"></i> ajs-icon-building</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"caret-right\"><i class=\"ajs-icon ajs-icon-caret-right\"></i> ajs-icon-caret-right</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"cercle\"><i class=\"ajs-icon ajs-icon-cercle\"></i> ajs-icon-cercle</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"check\"><i class=\"ajs-icon ajs-icon-check\"></i> ajs-icon-check</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-circle-down\"><i class=\"ajs-icon ajs-icon-chevron-circle-down\"></i> ajs-icon-chevron-circle-down</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-circle-left\"><i class=\"ajs-icon ajs-icon-chevron-circle-left\"></i> ajs-icon-chevron-circle-left</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-circle-right\"><i class=\"ajs-icon ajs-icon-chevron-circle-right\"></i> ajs-icon-chevron-circle-right</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-circle-up\"><i class=\"ajs-icon ajs-icon-chevron-circle-up\"></i> ajs-icon-chevron-circle-up</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-down\"><i class=\"ajs-icon ajs-icon-chevron-down\"></i> ajs-icon-chevron-down</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-left\"><i class=\"ajs-icon ajs-icon-chevron-left\"></i> ajs-icon-chevron-left</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-right\"><i class=\"ajs-icon ajs-icon-chevron-right\"></i> ajs-icon-chevron-right</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chevron-up\"><i class=\"ajs-icon ajs-icon-chevron-up\"></i> ajs-icon-chevron-up</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chromecast-active\"><i class=\"ajs-icon ajs-icon-chromecast-active\"></i> ajs-icon-chromecast-active</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"chromecast\"><i class=\"ajs-icon ajs-icon-chromecast\"></i> ajs-icon-chromecast</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"circle\"><i class=\"ajs-icon ajs-icon-circle\"></i> ajs-icon-circle</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"cog\"><i class=\"ajs-icon ajs-icon-cog\"></i> ajs-icon-cog</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"cogs\"><i class=\"ajs-icon ajs-icon-cogs\"></i> ajs-icon-cogs</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"comment\"><i class=\"ajs-icon ajs-icon-comment\"></i> ajs-icon-comment</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"compress\"><i class=\"ajs-icon ajs-icon-compress\"></i> ajs-icon-compress</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-backward\"><i class=\"ajs-icon ajs-icon-control-backward\"></i> ajs-icon-control-backward</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-fast-forward\"><i class=\"ajs-icon ajs-icon-control-fast-forward\"></i> ajs-icon-control-fast-forward</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-fast-rewind\"><i class=\"ajs-icon ajs-icon-control-fast-rewind\"></i> ajs-icon-control-fast-rewind</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-forward\"><i class=\"ajs-icon ajs-icon-control-forward\"></i> ajs-icon-control-forward</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-pause\"><i class=\"ajs-icon ajs-icon-control-pause\"></i> ajs-icon-control-pause</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-play\"><i class=\"ajs-icon ajs-icon-control-play\"></i> ajs-icon-control-play</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"control-rewind\"><i class=\"ajs-icon ajs-icon-control-rewind\"></i> ajs-icon-control-rewind</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-compress\"><i class=\"ajs-icon ajs-icon-controlbar-compress\"></i> ajs-icon-controlbar-compress</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-fullscreen\"><i class=\"ajs-icon ajs-icon-controlbar-fullscreen\"></i> ajs-icon-controlbar-fullscreen</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-pause\"><i class=\"ajs-icon ajs-icon-controlbar-pause\"></i> ajs-icon-controlbar-pause</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-play\"><i class=\"ajs-icon ajs-icon-controlbar-play\"></i> ajs-icon-controlbar-play</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-settings\"><i class=\"ajs-icon ajs-icon-controlbar-settings\"></i> ajs-icon-controlbar-settings</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume-left-off\"><i class=\"ajs-icon ajs-icon-controlbar-volume-left-off\"></i> ajs-icon-controlbar-volume-left-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume-left\"><i class=\"ajs-icon ajs-icon-controlbar-volume-left\"></i> ajs-icon-controlbar-volume-left</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume-min\"><i class=\"ajs-icon ajs-icon-controlbar-volume-min\"></i> ajs-icon-controlbar-volume-min</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume-off\"><i class=\"ajs-icon ajs-icon-controlbar-volume-off\"></i> ajs-icon-controlbar-volume-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume-right-off\"><i class=\"ajs-icon ajs-icon-controlbar-volume-right-off\"></i> ajs-icon-controlbar-volume-right-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume-right\"><i class=\"ajs-icon ajs-icon-controlbar-volume-right\"></i> ajs-icon-controlbar-volume-right</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume_max\"><i class=\"ajs-icon ajs-icon-controlbar-volume_max\"></i> ajs-icon-controlbar-volume_max</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"controlbar-volume_middle\"><i class=\"ajs-icon ajs-icon-controlbar-volume_middle\"></i> ajs-icon-controlbar-volume_middle</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"download\"><i class=\"ajs-icon ajs-icon-download\"></i> ajs-icon-download</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"eject\"><i class=\"ajs-icon ajs-icon-eject\"></i> ajs-icon-eject</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"ellipsis-h\"><i class=\"ajs-icon ajs-icon-ellipsis-h\"></i> ajs-icon-ellipsis-h</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"ellipsis-v\"><i class=\"ajs-icon ajs-icon-ellipsis-v\"></i> ajs-icon-ellipsis-v</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"eraser\"><i class=\"ajs-icon ajs-icon-eraser\"></i> ajs-icon-eraser</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"expand\"><i class=\"ajs-icon ajs-icon-expand\"></i> ajs-icon-expand</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"eye-off\"><i class=\"ajs-icon ajs-icon-eye-off\"></i> ajs-icon-eye-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"eye-on\"><i class=\"ajs-icon ajs-icon-eye-on\"></i> ajs-icon-eye-on</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"facetime\"><i class=\"ajs-icon ajs-icon-facetime\"></i> ajs-icon-facetime</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"female\"><i class=\"ajs-icon ajs-icon-female\"></i> ajs-icon-female</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"github\"><i class=\"ajs-icon ajs-icon-github\"></i> ajs-icon-github</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"information\"><i class=\"ajs-icon ajs-icon-information\"></i> ajs-icon-information</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-backward-0x\"><i class=\"ajs-icon ajs-icon-jogs-backward-0x\"></i> ajs-icon-jogs-backward-0x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-backward-1x\"><i class=\"ajs-icon ajs-icon-jogs-backward-1x\"></i> ajs-icon-jogs-backward-1x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-backward-2x\"><i class=\"ajs-icon ajs-icon-jogs-backward-2x\"></i> ajs-icon-jogs-backward-2x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-backward-3x\"><i class=\"ajs-icon ajs-icon-jogs-backward-3x\"></i> ajs-icon-jogs-backward-3x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-backward-4x\"><i class=\"ajs-icon ajs-icon-jogs-backward-4x\"></i> ajs-icon-jogs-backward-4x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-center\"><i class=\"ajs-icon ajs-icon-jogs-center\"></i> ajs-icon-jogs-center</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-fast-backward\"><i class=\"ajs-icon ajs-icon-jogs-fast-backward\"></i> ajs-icon-jogs-fast-backward</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-fast-forward\"><i class=\"ajs-icon ajs-icon-jogs-fast-forward\"></i> ajs-icon-jogs-fast-forward</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-forward-0x\"><i class=\"ajs-icon ajs-icon-jogs-forward-0x\"></i> ajs-icon-jogs-forward-0x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-forward-1x\"><i class=\"ajs-icon ajs-icon-jogs-forward-1x\"></i> ajs-icon-jogs-forward-1x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-forward-2x\"><i class=\"ajs-icon ajs-icon-jogs-forward-2x\"></i> ajs-icon-jogs-forward-2x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-forward-3x\"><i class=\"ajs-icon ajs-icon-jogs-forward-3x\"></i> ajs-icon-jogs-forward-3x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"jogs-forward-4x\"><i class=\"ajs-icon ajs-icon-jogs-forward-4x\"></i> ajs-icon-jogs-forward-4x</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"key\"><i class=\"ajs-icon ajs-icon-key\"></i> ajs-icon-key</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"legal\"><i class=\"ajs-icon ajs-icon-legal\"></i> ajs-icon-legal</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"list-close\"><i class=\"ajs-icon ajs-icon-list-close\"></i> ajs-icon-list-close</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"list-open\"><i class=\"ajs-icon ajs-icon-list-open\"></i> ajs-icon-list-open</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"lock-close\"><i class=\"ajs-icon ajs-icon-lock-close\"></i> ajs-icon-lock-close</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"lock-open\"><i class=\"ajs-icon ajs-icon-lock-open\"></i> ajs-icon-lock-open</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"male\"><i class=\"ajs-icon ajs-icon-male\"></i> ajs-icon-male</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"microphone-off\"><i class=\"ajs-icon ajs-icon-microphone-off\"></i> ajs-icon-microphone-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"microphone-on\"><i class=\"ajs-icon ajs-icon-microphone-on\"></i> ajs-icon-microphone-on</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"minus\"><i class=\"ajs-icon ajs-icon-minus\"></i> ajs-icon-minus</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"music\"><i class=\"ajs-icon ajs-icon-music\"></i> ajs-icon-music</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"picture\"><i class=\"ajs-icon ajs-icon-picture\"></i> ajs-icon-picture</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"plus\"><i class=\"ajs-icon ajs-icon-plus\"></i> ajs-icon-plus</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"power\"><i class=\"ajs-icon ajs-icon-power\"></i> ajs-icon-power</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"refresh\"><i class=\"ajs-icon ajs-icon-refresh\"></i> ajs-icon-refresh</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"remove\"><i class=\"ajs-icon ajs-icon-remove\"></i> ajs-icon-remove</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"reorder\"><i class=\"ajs-icon ajs-icon-reorder\"></i> ajs-icon-reorder</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"screenshot\"><i class=\"ajs-icon ajs-icon-screenshot\"></i> ajs-icon-screenshot</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"scrubber-cursor\"><i class=\"ajs-icon ajs-icon-scrubber-cursor\"></i> ajs-icon-scrubber-cursor</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"search\"><i class=\"ajs-icon ajs-icon-search\"></i> ajs-icon-search</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"sign-in\"><i class=\"ajs-icon ajs-icon-sign-in\"></i> ajs-icon-sign-in</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"sign-out\"><i class=\"ajs-icon ajs-icon-sign-out\"></i> ajs-icon-sign-out</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"sort\"><i class=\"ajs-icon ajs-icon-sort\"></i> ajs-icon-sort</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"sound-link-off\"><i class=\"ajs-icon ajs-icon-sound-link-off\"></i> ajs-icon-sound-link-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"sound-link-on\"><i class=\"ajs-icon ajs-icon-sound-link-on\"></i> ajs-icon-sound-link-on</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"stop\"><i class=\"ajs-icon ajs-icon-stop\"></i> ajs-icon-stop</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"transcription\"><i class=\"ajs-icon ajs-icon-transcription\"></i> ajs-icon-transcription</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"volume-down\"><i class=\"ajs-icon ajs-icon-volume-down\"></i> ajs-icon-volume-down</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"volume-off\"><i class=\"ajs-icon ajs-icon-volume-off\"></i> ajs-icon-volume-off</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"volume-up\"><i class=\"ajs-icon ajs-icon-volume-up\"></i> ajs-icon-volume-up</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"zoom-in\"><i class=\"ajs-icon ajs-icon-zoom-in\"></i> ajs-icon-zoom-in</div>\n\t\t\t\n\t\t\t\t<div class=\"icons__item\" data-name=\"zoom-out\"><i class=\"ajs-icon ajs-icon-zoom-out\"></i> ajs-icon-zoom-out</div>\n\t\t\t\n\t\t</div>\n\n\t\t\n\n\t\t<h1>Usage</h1>\n\t\t<pre><code>&lt;i class=&quot;ajs-icon ajs-icon-<span id=\"name\">name</span>&quot;&gt;&lt;/i&gt;</code></pre>\n\t\t\n\n\t\t<footer>Generated by <a href=\"https://github.com/sapegin/grunt-webfont\">grunt-webfont</a>.</footer>\n\n\t\t<script>\n\t\t(function() {\n\t\t\tdocument.getElementById('icons').onclick = function(e) {\n\t\t\t\te = e || window.event;\n\t\t\t\tvar name = e.target.getAttribute('data-name') || e.target.parentNode.getAttribute('data-name');\n\t\t\t\tdocument.getElementById('name').innerHTML = name;\n\t\t\t\tdocument.getElementById('name2').innerHTML = name;\n\t\t\t}\n\t\t})();\n\t\t</script>\n\t</body>\n</html>\n"
  },
  {
    "path": "samples/css/amalia-js-demo.css",
    "content": ".header {\n    margin-top: 80px;\n    text-align: center;\n}\n\n.page-footer {\n    margin-bottom: 0px;\n    padding: 20px;\n    margin-top: 40px\n}\n\nh1.title {\n    margin-top: 25px;\n    padding: 0px;\n    padding-bottom: 0px;\n    font-weight: bold;\n    font-size: 40px;\n    border-bottom: 1px solid #eaeaea;\n}\n\nh3.title {\n    margin-top: 40px;\n    margin-bottom: 20px;\n    font-weight: 500;\n    border-bottom: #eee solid 1px;\n    padding-bottom: 10px;\n    margin-left: 10px;\n}\n\nh4.title {\n\n}\n\n.smallpre {\n    font-size: 12px;\n}\n\n.navbar .nav>li>a {\n    font-size: 14px;\n    line-height: 22px;\n    font-weight: 500;\n    text-transform: uppercase;\n}\n\n.navbar-brand {\n    font-size: 24px;\n}\n\n.contact h1.title {\n    margin-top: 0px;\n    border-bottom: 1px solid #eee;\n    padding-bottom: 10px;\n    font-weight: bold;\n}\n\n.navbar-header .navbar-brand {\n    background-image: url(images/amalia_Logo-unique-white-mini.png);\n    background-repeat: no-repeat;\n    padding-left: 55px;\n    background-position: center left;\n}\n\n.docssidebar {\n    top: 100px;\n}\n.download-version-text\n{\n    min-height: 120px;\n}\n\n"
  },
  {
    "path": "samples/css/default.css",
    "content": "a {\n    color: #008cba;\n    text-decoration: none;\n}\na {\n    background-color: transparent;\n}\n* {\n    -webkit-box-sizing: none;\n    -moz-box-sizing: none;\n    box-sizing: none;\n}\nbody\n{\n    font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;\n}\n.container\n{\n    box-sizing: border-box;\n    color: rgb(34, 34, 34);\n    display: block;\n    font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;\n    font-size: 15px;\n    line-height: 21px;\n    padding-left: 15px;\n    padding-right: 15px;\n}\nh1.title\n{\n    border-bottom-color: rgb(234, 234, 234);\n    border-bottom-style: solid;\n    border-bottom-width: 1px;\n    box-sizing: border-box;\n    color: rgb(34, 34, 34);\n    display: block;\n    font-size: 40px;\n    font-weight: bold;\n    height: 45px;\n    line-height: 44px;\n    margin-bottom: 10.5px;\n    margin-left: 0px;\n    margin-right: 0px;\n    margin-top: 25px;\n    padding-bottom: 0px;\n    padding-left: 0px;\n    padding-right: 0px;\n    padding-top: 0px;    \n}\n\n\n\npre.config \n{\n    display: block;\n    padding: 9.5px;\n    margin: 0 0 10px;\n    font-size: 13px;\n    line-height: 1.42857143;\n    word-break: break-all;\n    word-wrap: break-word;\n    color: #333;\n    background-color: #f5f5f5;\n    border: 1px solid #ccc;\n    border-radius: 4px;\n}\n\n.footer\n{\n    clear: both;\n}"
  },
  {
    "path": "samples-data/examples/json/amalia-simple01.json",
    "content": "{\r\n    \"localisation\": [\r\n        {\r\n            \"sublocalisations\": {\r\n                \"localisation\": [\r\n                    {\r\n                        \"label\": \"A demo label !\",\r\n                        \"tc\": \"00:00:30.0000\",\r\n                        \"tclevel\": 1\r\n                    }\r\n                ]\r\n            },\r\n            \"type\": \"fake\",\r\n            \"tcin\": \"00:00:00.0000\",\r\n            \"tcout\": \"00:01:00.0000\",\r\n            \"tclevel\": 0\r\n        }\r\n    ],\r\n    \"id\": \"amalia-simple01\",\r\n    \"type\": \"fake\",\r\n    \"algorithm\": \"demo-json-generator\",\r\n    \"processor\": \"Ina Research Department - N. HERVE\",\r\n    \"processed\": 1418900533632,\r\n    \"version\": 1\r\n}"
  },
  {
    "path": "samples-data/examples/json/amalia01-ball.json",
    "content": "{\r\n    \"localisation\": [\r\n        {\r\n            \"sublocalisations\": {\r\n                \"localisation\": [\r\n                    {\r\n                        \"tcin\": \"00:00:01.6800\",\r\n                        \"tcout\": \"00:00:03.3600\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"tcin\": \"00:00:05.0400\",\r\n                        \"tcout\": \"00:00:06.7200\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"tcin\": \"00:00:08.4000\",\r\n                        \"tcout\": \"00:00:10.0800\",\r\n                        \"tclevel\": 1\r\n                    }\r\n                ]\r\n            },\r\n            \"type\": \"segments\",\r\n            \"tcin\": \"00:00:00.0000\",\r\n            \"tcout\": \"00:00:15.0000\",\r\n            \"tclevel\": 0\r\n        }\r\n    ],\r\n    \"id\": \"ball-amalia01\",\r\n    \"type\": \"segments\",\r\n    \"algorithm\": \"demo-video-generator\",\r\n    \"processor\": \"Ina Research Department - N. HERVE\",\r\n    \"processed\": 1421141589286,\r\n    \"version\": 1\r\n}"
  },
  {
    "path": "samples-data/examples/json/amalia01-events.json",
    "content": "{\r\n    \"localisation\": [\r\n        {\r\n            \"sublocalisations\": {\r\n                \"localisation\": [\r\n                    {\r\n                        \"label\": \"Start\",\r\n                        \"tc\": \"00:00:01.6800\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"label\": \"Ping\",\r\n                        \"tc\": \"00:00:03.3600\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"label\": \"Ping\",\r\n                        \"tc\": \"00:00:05.0400\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"label\": \"Ping\",\r\n                        \"tc\": \"00:00:06.7200\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"label\": \"Ping\",\r\n                        \"tc\": \"00:00:08.4000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"label\": \"Ping\",\r\n                        \"tc\": \"00:00:10.0800\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"label\": \"Pop\",\r\n                        \"tc\": \"00:00:12.0000\",\r\n                        \"tclevel\": 1\r\n                    }\r\n                ]\r\n            },\r\n            \"type\": \"events\",\r\n            \"tcin\": \"00:00:00.0000\",\r\n            \"tcout\": \"00:00:15.0000\",\r\n            \"tclevel\": 0\r\n        }\r\n    ],\r\n    \"id\": \"events-amalia01\",\r\n    \"type\": \"events\",\r\n    \"algorithm\": \"demo-video-generator\",\r\n    \"processor\": \"Ina Research Department - N. HERVE\",\r\n    \"processed\": 1421141589279,\r\n    \"version\": 1\r\n}"
  },
  {
    "path": "samples-data/examples/json/amalia01-kf.json",
    "content": "{\r\n    \"localisation\": [\r\n        {\r\n            \"sublocalisations\": {\r\n                \"localisation\": [\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_25.jpg\",\r\n                        \"tc\": \"00:00:01.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_75.jpg\",\r\n                        \"tc\": \"00:00:03.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_125.jpg\",\r\n                        \"tc\": \"00:00:05.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_175.jpg\",\r\n                        \"tc\": \"00:00:07.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_225.jpg\",\r\n                        \"tc\": \"00:00:09.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_275.jpg\",\r\n                        \"tc\": \"00:00:11.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"thumb\": \"samples-data/examples/kf/amalia01/f_325.jpg\",\r\n                        \"tc\": \"00:00:13.0000\",\r\n                        \"tclevel\": 1\r\n                    }\r\n                ]\r\n            },\r\n            \"type\": \"keyframes\",\r\n            \"tcin\": \"00:00:00.0000\",\r\n            \"tcout\": \"00:00:15.0000\",\r\n            \"tclevel\": 0\r\n        }\r\n    ],\r\n    \"id\": \"kf-amalia01\",\r\n    \"type\": \"keyframes\",\r\n    \"algorithm\": \"demo-video-generator\",\r\n    \"processor\": \"Ina Research Department - N. HERVE\",\r\n    \"processed\": 1421141589291,\r\n    \"version\": 1\r\n}"
  },
  {
    "path": "samples-data/examples/json/amalia01-overlay.json",
    "content": "{\n  \"localisation\": [\n    {\n      \"sublocalisations\": {\n        \"localisation\": [\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.2,\n                \"y\": 0.5\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:00.0000\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.2,\n                \"y\": 0.5\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:01.6800\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.2857142857142857,\n                \"y\": 0.3\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:03.3600\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.37142857142857144,\n                \"y\": 0.7\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:05.0400\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.4571428571428572,\n                \"y\": 0.3\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:06.7200\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.5428571428571429,\n                \"y\": 0.7\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:08.4000\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.6285714285714287,\n                \"y\": 0.3\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:10.0800\",\n            \"tclevel\": 1\n          },\n          {\n            \"shape\": {\n              \"t\": \"rectangle\",\n              \"c\": {\n                \"x\": 0.7142857142857144,\n                \"y\": 0.7\n              },\n              \"rx\": 0.03654970760233918,\n              \"ry\": 0.06510416666666667,\n              \"o\": 0.0\n            },\n            \"tc\": \"00:00:12.0000\",\n            \"tclevel\": 1\n          }\n        ]\n      },\n      \"label\": \"EllipseWithBoudingBox-1\",\n      \"tcin\": \"00:00:00.0000\",\n      \"tcout\": \"00:00:12.0000\",\n      \"tclevel\": 0\n    }\n  ],\n  \"id\": \"track-amalia01-1\",\n  \"type\": \"visual_tracking\",\n  \"algorithm\": \"demo-video-generator\",\n  \"processor\": \"Ina Research Department - N. HERVE\",\n  \"processed\": 1432288449928,\n  \"version\": 1\n}\n"
  },
  {
    "path": "samples-data/examples/json/amalia01-text.json",
    "content": "{\r\n    \"localisation\": [\r\n        {\r\n            \"sublocalisations\": {\r\n                \"localisation\": [\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"<b>This</b> is a demonstration video\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:00.0000\",\r\n                        \"tcout\": \"00:00:01.6800\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"with a red ball\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:01.6800\",\r\n                        \"tcout\": \"00:00:03.3600\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"ping\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:03.3600\",\r\n                        \"tcout\": \"00:00:05.0400\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"pong\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:05.0400\",\r\n                        \"tcout\": \"00:00:06.7200\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"ping\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:06.7200\",\r\n                        \"tcout\": \"00:00:08.4000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"pong\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:08.4000\",\r\n                        \"tcout\": \"00:00:10.0800\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"ping\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:10.0800\",\r\n                        \"tcout\": \"00:00:12.0000\",\r\n                        \"tclevel\": 1\r\n                    },\r\n                    {\r\n                        \"data\": {\r\n                            \"text\": [\r\n                                \"... the ball has disappeared !\"\r\n                            ]\r\n                        },\r\n                        \"tcin\": \"00:00:12.0000\",\r\n                        \"tcout\": \"00:00:15.0000\",\r\n                        \"tclevel\": 1\r\n                    }\r\n                ]\r\n            },\r\n            \"type\": \"text\",\r\n            \"tcin\": \"00:00:00.0000\",\r\n            \"tcout\": \"00:00:15.0000\",\r\n            \"tclevel\": 0\r\n        }\r\n    ],\r\n    \"id\": \"text-amalia01\",\r\n    \"type\": \"text\",\r\n    \"algorithm\": \"demo-video-generator\",\r\n    \"processor\": \"Ina Research Department - N. HERVE\",\r\n    \"processed\": 1421141589288,\r\n    \"version\": 1\r\n}"
  },
  {
    "path": "src/.jshintrc",
    "content": "{\n  \"curly\": true,\n  \"eqeqeq\": true,\n  \"immed\": true,\n  \"latedef\": true,\n  \"newcap\": true,\n  \"noarg\": true,\n  \"sub\": true,\n  \"undef\": true,\n  \"unused\": true,\n  \"boss\": true,\n  \"eqnull\": true,\n  \"browser\": true,\n  \"predef\": [\n    \"jQuery\",\n    \"$\",\n    \"fr\",\n    \"google\",\n    \"_PlayerAmaliaVersion_\",\n    \"_PlayerAmaliaHomepage_\",\n    \"Raphael\",\n    \"MediaPlayer\",\n    \"Dash\",\n    \"AudioContext\",\n    \"YT\",\n    \"chrome\",\n    \"d3\",\n    \"nv\"\n  ]\n}\n"
  },
  {
    "path": "src/LICENSE",
    "content": "Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "src/THIRD_PARTY_LICENSES",
    "content": "Third-Party Licenses : \n\t-jQuery : easy DOM manipulation framework, MIT license, http://jquery.com        \n\t-jQueryClass : simulated inheritance in javascript, MIT license, http://v3.javascriptmvc.com/docs/jQuery.Class.html#&who=jQuery.Class\n\t-jQueryUI : user interface stuff on top of jQuery, MIT license, http://jqueryui.com\n\t-jQueryUI Touch Punch : touch event support for jQuery UI, MIT or GPL Version 2 licenses, http://touchpunch.furf.com\n\t-jQuery knob : a knob for jQuery,MIT or GPL licenses, http://anthonyterrien.com/knob\n\t-Raphaël : a vector graphics Javascript library, MIT License, http://raphaeljs.com\n\t-Bootstrap : responsive design library, MIT License, http://getbootstrap.com\n\t-Font Awesome : iconic font, MIT License , http://fortawesome.github.io/Font-Awesome/"
  },
  {
    "path": "src/assets/fonts/ajs-webfont.afm",
    "content": "StartFontMetrics 2.0\nComment Generated by FontForge 20090622\nComment Creation Date: Thu Jul  2 11:16:15 2015\nFontName ajs-webfont\nFullName ajs-webfont\nFamilyName ajs-webfont\nWeight Medium\nNotice ()\nItalicAngle 0\nIsFixedPitch true\nUnderlinePosition -51.2\nUnderlineThickness 25.6\nVersion 001.000\nEncodingScheme ISO10646-1\nFontBBox 0 -109 1000 876\nDescender -2147483648\nStartCharMetrics 98\nC -1 ; WX 1000 ; N uniF101 ; B 127 156 898 805 ;\nC -1 ; WX 1000 ; N uniF102 ; B 260 88 740 662 ;\nC -1 ; WX 1000 ; N uniF103 ; B 162 416 838 658 ;\nC -1 ; WX 1000 ; N uniF104 ; B 379 199 621 875 ;\nC -1 ; WX 1000 ; N uniF105 ; B 186 199 814 876 ;\nC -1 ; WX 1000 ; N uniF106 ; B 234 199 766 875 ;\nC -1 ; WX 1000 ; N uniF107 ; B 391 343 609 731 ;\nC -1 ; WX 1000 ; N uniF108 ; B 207 312 793 762 ;\nC -1 ; WX 1000 ; N uniF109 ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF10A ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF10B ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF10C ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF10D ; B 206 351 794 723 ;\nC -1 ; WX 1000 ; N uniF10E ; B 314 243 686 831 ;\nC -1 ; WX 1000 ; N uniF10F ; B 314 243 686 831 ;\nC -1 ; WX 1000 ; N uniF110 ; B 206 351 794 723 ;\nC -1 ; WX 1000 ; N uniF111 ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF112 ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF113 ; B 137 204 863 870 ;\nC -1 ; WX 1000 ; N uniF114 ; B 162 247 838 827 ;\nC -1 ; WX 1000 ; N uniF115 ; B 215 252 785 822 ;\nC -1 ; WX 1000 ; N uniF116 ; B 209 246 791 828 ;\nC -1 ; WX 1000 ; N uniF117 ; B 162 246 838 828 ;\nC -1 ; WX 1000 ; N uniF118 ; B 162 246 838 828 ;\nC -1 ; WX 1000 ; N uniF119 ; B 209 246 791 828 ;\nC -1 ; WX 1000 ; N uniF11A ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF11B ; B 234 244 766 830 ;\nC -1 ; WX 1000 ; N uniF11C ; B 306 246 694 828 ;\nC -1 ; WX 1000 ; N uniF11D ; B 109 -18 892 765 ;\nC -1 ; WX 1000 ; N uniF11E ; B 106 -18 888 765 ;\nC -1 ; WX 1000 ; N uniF11F ; B 60 -69 941 812 ;\nC -1 ; WX 1000 ; N uniF120 ; B 55 -68 935 813 ;\nC -1 ; WX 1000 ; N uniF121 ; B 104 -23 897 770 ;\nC -1 ; WX 1000 ; N uniF122 ; B 43 118 859 819 ;\nC -1 ; WX 1000 ; N uniF123 ; B 141 106 856 807 ;\nC -1 ; WX 1000 ; N uniF124 ; B 148 118 862 819 ;\nC -1 ; WX 1000 ; N uniF125 ; B 74 119 927 820 ;\nC -1 ; WX 1000 ; N uniF126 ; B 148 118 958 819 ;\nC -1 ; WX 1000 ; N uniF127 ; B 141 112 856 813 ;\nC -1 ; WX 1000 ; N uniF128 ; B 15 113 986 819 ;\nC -1 ; WX 1000 ; N uniF129 ; B 76 115 917 817 ;\nC -1 ; WX 1000 ; N uniF12A ; B 186 247 814 827 ;\nC -1 ; WX 1000 ; N uniF12B ; B 209 294 791 780 ;\nC -1 ; WX 1000 ; N uniF12C ; B 427 271 573 803 ;\nC -1 ; WX 1000 ; N uniF12D ; B 234 464 766 610 ;\nC -1 ; WX 1000 ; N uniF12E ; B 137 295 863 779 ;\nC -1 ; WX 1000 ; N uniF12F ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF130 ; B 162 283 838 791 ;\nC -1 ; WX 1000 ; N uniF131 ; B 162 319 838 755 ;\nC -1 ; WX 1000 ; N uniF132 ; B 162 295 838 779 ;\nC -1 ; WX 1000 ; N uniF133 ; B 258 205 742 869 ;\nC -1 ; WX 1000 ; N uniF134 ; B 210 254 790 820 ;\nC -1 ; WX 1000 ; N uniF135 ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF136 ; B 0 0 0 0 ;\nC -1 ; WX 1000 ; N uniF137 ; B 760 360 887 594 ;\nC -1 ; WX 1000 ; N uniF138 ; B 595 306 906 610 ;\nC -1 ; WX 1000 ; N uniF139 ; B 345 247 887 708 ;\nC -1 ; WX 1000 ; N uniF13A ; B 114 190 887 760 ;\nC -1 ; WX 1000 ; N uniF13B ; B 162 27 860 401 ;\nC -1 ; WX 1000 ; N uniF13C ; B 113 190 906 760 ;\nC -1 ; WX 1000 ; N uniF13D ; B 111 207 904 777 ;\nC -1 ; WX 1000 ; N uniF13E ; B 111 204 882 774 ;\nC -1 ; WX 1000 ; N uniF13F ; B 111 372 238 606 ;\nC -1 ; WX 1000 ; N uniF140 ; B 111 337 422 641 ;\nC -1 ; WX 1000 ; N uniF141 ; B 111 259 652 720 ;\nC -1 ; WX 1000 ; N uniF142 ; B 111 207 884 777 ;\nC -1 ; WX 1000 ; N uniF143 ; B 21 -45 975 818 ;\nC -1 ; WX 1000 ; N uniF144 ; B 173 210 827 864 ;\nC -1 ; WX 1000 ; N uniF145 ; B 75 207 913 788 ;\nC -1 ; WX 1000 ; N uniF146 ; B 71 214 908 795 ;\nC -1 ; WX 1000 ; N uniF147 ; B 282 271 718 803 ;\nC -1 ; WX 1000 ; N uniF148 ; B 186 271 814 803 ;\nC -1 ; WX 1000 ; N uniF149 ; B 306 205 694 869 ;\nC -1 ; WX 1000 ; N uniF14A ; B 239 223 761 851 ;\nC -1 ; WX 1000 ; N uniF14B ; B 282 223 718 851 ;\nC -1 ; WX 1000 ; N uniF14C ; B 234 464 766 610 ;\nC -1 ; WX 1000 ; N uniF14D ; B 210 223 790 851 ;\nC -1 ; WX 1000 ; N uniF14E ; B 137 247 863 827 ;\nC -1 ; WX 1000 ; N uniF14F ; B 234 271 766 803 ;\nC -1 ; WX 1000 ; N uniF150 ; B 210 223 790 851 ;\nC -1 ; WX 1000 ; N uniF151 ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF152 ; B 275 312 725 762 ;\nC -1 ; WX 1000 ; N uniF153 ; B 210 295 790 779 ;\nC -1 ; WX 1000 ; N uniF154 ; B 176 335 824 771 ;\nC -1 ; WX 1000 ; N uniF155 ; B 16 -109 984 859 ;\nC -1 ; WX 1000 ; N uniF156 ; B 186 223 814 851 ;\nC -1 ; WX 1000 ; N uniF157 ; B 210 295 790 779 ;\nC -1 ; WX 1000 ; N uniF158 ; B 204 295 796 779 ;\nC -1 ; WX 1000 ; N uniF159 ; B 306 271 694 803 ;\nC -1 ; WX 1000 ; N uniF15A ; B 0 323 1000 875 ;\nC -1 ; WX 1000 ; N uniF15B ; B 0 645 1000 876 ;\nC -1 ; WX 1000 ; N uniF15C ; B 210 247 790 827 ;\nC -1 ; WX 1000 ; N uniF15D ; B 11 -105 986 858 ;\nC -1 ; WX 1000 ; N uniF15E ; B 282 307 718 767 ;\nC -1 ; WX 1000 ; N uniF15F ; B 355 307 645 767 ;\nC -1 ; WX 1000 ; N uniF160 ; B 186 268 814 806 ;\nC -1 ; WX 1000 ; N uniF161 ; B 186 223 814 851 ;\nC -1 ; WX 1000 ; N uniF162 ; B 186 223 814 851 ;\nEndCharMetrics\nEndFontMetrics\n"
  },
  {
    "path": "src/assets/less/ajs-animations.less",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n@keyframes ajs-hatch {\n  0% {\n    transform: rotate(0deg) scaleY(0.6);\n  }\n  20% {\n    transform: rotate(-2deg) scaleY(1.05);\n  }\n  35% {\n    transform: rotate(2deg) scaleY(1);\n  }\n  50% {\n    transform: rotate(-2deg);\n  }\n  65% {\n    transform: rotate(1deg);\n  }\n  80% {\n    transform: rotate(-1deg);\n  }\n  100% {\n    transform: rotate(0deg);\n  }\n}\n\n@-webkit-keyframes ajs-hatch {\n  0% {\n    -webkit-transform: rotate(0deg) scaleY(0.6);\n  }\n  20% {\n    -webkit-transform: rotate(-2deg) scaleY(1.05);\n  }\n  35% {\n    -webkit-transform: rotate(2deg) scaleY(1);\n  }\n  50% {\n    -webkit-transform: rotate(-2deg);\n  }\n  65% {\n    -webkit-transform: rotate(1deg);\n  }\n  80% {\n    -webkit-transform: rotate(-1deg);\n  }\n  100% {\n    -webkit-transform: rotate(0deg);\n  }\n}\n\n@keyframes ajs-bounce {\n  0% {\n    transform: translateY(0%) scaleY(0.6);\n  }\n  60% {\n    transform: translateY(-100%) scaleY(1.1);\n  }\n  70% {\n    transform: translateY(0%) scaleY(0.95) scaleX(1.05);\n  }\n  80% {\n    transform: translateY(0%) scaleY(1.05) scaleX(1);\n  }\n  90% {\n    transform: translateY(0%) scaleY(0.95) scaleX(1);\n  }\n  100% {\n    transform: translateY(0%) scaleY(1) scaleX(1);\n  }\n}\n\n@-webkit-keyframes ajs-bounce {\n  0% {\n    -webkit-transform: translateY(0%) scaleY(0.6);\n  }\n  60% {\n    -webkit-transform: translateY(-100%) scaleY(1.1);\n  }\n  70% {\n    -webkit-transform: translateY(0%) scaleY(0.95) scaleX(1.05);\n  }\n  80% {\n    -webkit-transform: translateY(0%) scaleY(1.05) scaleX(1);\n  }\n  90% {\n    -webkit-transform: translateY(0%) scaleY(0.95) scaleX(1);\n  }\n  100% {\n    -webkit-transform: translateY(0%) scaleY(1) scaleX(1);\n  }\n}\n\n@keyframes ajs-pulse {\n  0% {\n    transform: scale(0.9);\n    opacity: 0.7;\n  }\n  50% {\n    transform: scale(1);\n    opacity: 1;\n  }\n  100% {\n    transform: scale(0.9);\n    opacity: 0.7;\n  }\n}\n\n@-webkit-keyframes ajs-pulse {\n  0% {\n    -webkit-transform: scale(0.95);\n    opacity: 0.7;\n  }\n  50% {\n    -webkit-transform: scale(1);\n    opacity: 1;\n  }\n  100% {\n    -webkit-transform: scale(0.95);\n    opacity: 0.7;\n  }\n}"
  },
  {
    "path": "src/assets/less/ajs-webfont.less",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n\n@font-face {\n  font-family: \"ajs-webfont\";\n  src: url(\"../fonts/ajs-webfont.eot?93a8957d9da533b23877909fab6d8fc2\");\n  font-weight: normal;\n  font-style: normal;\n}\n\n@font-face {\n  font-family: \"ajs-webfont\";\n  src: url(\"../fonts/ajs-webfont.eot?93a8957d9da533b23877909fab6d8fc2\");\n  src: url(\"../fonts/ajs-webfont.eot?#iefix\") format(\"embedded-opentype\"),\n  url(\"data:application/x-font-woff;charset=utf-8;base64,gAEwAwAAJSFQUy1BZG9iZUZvbnQtMS4wOiBhanMtd2ViZm9udCAwMDEuMDAwCiUlVGl0bGU6IGFqcy13ZWJmb250CiVWZXJzaW9uOiAwMDEuMDAwCiUlQ3JlYXRpb25EYXRlOiBUaHUgSnVsICAyIDExOjE2OjE1IDIwMTUKJSVDcmVhdG9yOiByb290CiUgMjAxNS03LTI6IENyZWF0ZWQuCiUgR2VuZXJhdGVkIGJ5IEZvbnRGb3JnZSAyMDA5MDYyMiAoaHR0cDovL2ZvbnRmb3JnZS5zZi5uZXQvKQolJUVuZENvbW1lbnRzCgoxMCBkaWN0IGJlZ2luCi9Gb250VHlwZSAxIGRlZgovRm9udE1hdHJpeCBbMC4wMDE5NTMxMiAwIDAgMC4wMDE5NTMxMiAwIDAgXXJlYWRvbmx5IGRlZgovRm9udE5hbWUgL2Fqcy13ZWJmb250IGRlZgovRm9udEJCb3ggezAgLTU2IDUxMiA0NDkgfXJlYWRvbmx5IGRlZgovUGFpbnRUeXBlIDAgZGVmCi9Gb250SW5mbyAxMSBkaWN0IGR1cCBiZWdpbgogL3ZlcnNpb24gKDAwMS4wMDApIHJlYWRvbmx5IGRlZgogL05vdGljZSAoKSByZWFkb25seSBkZWYKIC9GdWxsTmFtZSAoYWpzLXdlYmZvbnQpIHJlYWRvbmx5IGRlZgogL0ZhbWlseU5hbWUgKGFqcy13ZWJmb250KSByZWFkb25seSBkZWYKIC9XZWlnaHQgKE1lZGl1bSkgcmVhZG9ubHkgZGVmCiAvRlNUeXBlIDAgZGVmCiAvSXRhbGljQW5nbGUgMCBkZWYKIC9pc0ZpeGVkUGl0Y2ggdHJ1ZSBkZWYKIC9VbmRlcmxpbmVQb3NpdGlvbiAtNTEuMiBkZWYKIC9VbmRlcmxpbmVUaGlja25lc3MgMjUuNiBkZWYKIC9hc2NlbnQgNDQ4IGRlZgplbmQgcmVhZG9ubHkgZGVmCi9FbmNvZGluZyBTdGFuZGFyZEVuY29kaW5nIGRlZgpjdXJyZW50ZGljdCBlbmQKY3VycmVudGZpbGUgZWV4ZWMKgAJTiAAAdD+EE/NjbKhan/77ULS7JzAqWmP5MohOGL9RU602BTA30cbNBClK9qNWEtuRCKyFFMtcSoRplxt1oJ+eZiBosGhUkOqMc/Le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VmGAWOxyl57FnCwoyDsUwdPoxKviD1PIpindSxH3cCWlvpiPPGCOKzJ+D0g5Y9dCSQVQPyuijuHG4HqG0fJHOlO9P0SlzxXe7jWYhGDWdu5lkFeflsSDl/mb2+PvTduaKo6x0+Ocu6KTSWQwAkswAZG7llmCILjPkJu/kDfnYFUETtVzeT4unx0ZMM4FmZG387n6IhOqAIX7fDKUHQe8LuQx6EfKGZpOBmzG8hyiuL/NXnbNapKHaDh4/6k6h0x0TztFEa7gytWMNN48OsnQjgPXIdOu8aXYeu/w5AeIp8kmpkJsU6KL3FrtRCVQTvNEgVxsFQy6sFddxvd6QBQQyW6GmIIcSsdsnG43qCkQIU1zcnwJ8izGQU4X+M706LVLCOLhiwrRt72Fsl1E/5NNvSm1enKvgW6oMi+CiR2W/iejx8YX7Dd1lvcb8dyOi0uJhK+e8smf2JTjNjM1zpmYz5eDEE6bO/+q4J/YKMiBFGzJdUCrpMICXYv/WnV0VeuAEGYvH00uP+bz4PD+6Gx6CRVGFq7Jd/GzRby0p4hSqoyQm/T0Ybapj0KuySwKYsq/zaVmyJYdHkPVMfyoDIsYK3c308ETrGeKDM2WuI3TrCvMqvCfZ62fKH4Z0nqzG1JztS8gBdWEiKMu2EXJMK2oKAZEEObj2/qq+bznG4nx1pkW2rVI1QxIcWuDOVZRMrIdkfDAl9V5TfAUF9O94rpNKEnR4XbLvQzTYGqXA2Zay2OkeoVP1wC6owsCJXKBok3SPentiOa8Mc2itEziS7dgCYptC6agDL5mZumspcWquWErvTu5g/v3iS9fV7QwmNzQaguPgAEVAgAACjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMApjbGVhcnRvbWFyawqAAw==\") format(\"woff\"),\n  url(\"../fonts/ajs-webfont.ttf?93a8957d9da533b23877909fab6d8fc2\") format(\"truetype\");\n  font-weight: normal;\n  font-style: normal;\n}\n\n.ajs-icon {\n  font-family: \"ajs-webfont\";\n  display: inline-block;\n  vertical-align: middle;\n  line-height: 1;\n  font-weight: normal;\n  font-style: normal;\n  speak: none;\n  text-decoration: inherit;\n  text-transform: none;\n  text-rendering: optimizeLegibility;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n// Icons\n\n.ajs-icon-add-whole-segment() {\n  &:before {\n    content: \"\\f101\";\n  }\n\n}\n\n.ajs-icon-add-whole-segment {\n  .ajs-icon-add-whole-segment();\n}\n\n.ajs-icon-amalia-js() {\n  &:before {\n    content: \"\\f102\";\n  }\n\n}\n\n.ajs-icon-amalia-js {\n  .ajs-icon-amalia-js();\n}\n\n.ajs-icon-arrows-h() {\n  &:before {\n    content: \"\\f103\";\n  }\n\n}\n\n.ajs-icon-arrows-h {\n  .ajs-icon-arrows-h();\n}\n\n.ajs-icon-arrows-v() {\n  &:before {\n    content: \"\\f104\";\n  }\n\n}\n\n.ajs-icon-arrows-v {\n  .ajs-icon-arrows-v();\n}\n\n.ajs-icon-bell() {\n  &:before {\n    content: \"\\f105\";\n  }\n\n}\n\n.ajs-icon-bell {\n  .ajs-icon-bell();\n}\n\n.ajs-icon-building() {\n  &:before {\n    content: \"\\f106\";\n  }\n\n}\n\n.ajs-icon-building {\n  .ajs-icon-building();\n}\n\n.ajs-icon-caret-right() {\n  &:before {\n    content: \"\\f107\";\n  }\n\n}\n\n.ajs-icon-caret-right {\n  .ajs-icon-caret-right();\n}\n\n.ajs-icon-check() {\n  &:before {\n    content: \"\\f108\";\n  }\n\n}\n\n.ajs-icon-check {\n  .ajs-icon-check();\n}\n\n.ajs-icon-chevron-circle-down() {\n  &:before {\n    content: \"\\f109\";\n  }\n\n}\n\n.ajs-icon-chevron-circle-down {\n  .ajs-icon-chevron-circle-down();\n}\n\n.ajs-icon-chevron-circle-left() {\n  &:before {\n    content: \"\\f10a\";\n  }\n\n}\n\n.ajs-icon-chevron-circle-left {\n  .ajs-icon-chevron-circle-left();\n}\n\n.ajs-icon-chevron-circle-right() {\n  &:before {\n    content: \"\\f10b\";\n  }\n\n}\n\n.ajs-icon-chevron-circle-right {\n  .ajs-icon-chevron-circle-right();\n}\n\n.ajs-icon-chevron-circle-up() {\n  &:before {\n    content: \"\\f10c\";\n  }\n\n}\n\n.ajs-icon-chevron-circle-up {\n  .ajs-icon-chevron-circle-up();\n}\n\n.ajs-icon-chevron-down() {\n  &:before {\n    content: \"\\f10d\";\n  }\n\n}\n\n.ajs-icon-chevron-down {\n  .ajs-icon-chevron-down();\n}\n\n.ajs-icon-chevron-left() {\n  &:before {\n    content: \"\\f10e\";\n  }\n\n}\n\n.ajs-icon-chevron-left {\n  .ajs-icon-chevron-left();\n}\n\n.ajs-icon-chevron-right() {\n  &:before {\n    content: \"\\f10f\";\n  }\n\n}\n\n.ajs-icon-chevron-right {\n  .ajs-icon-chevron-right();\n}\n\n.ajs-icon-chevron-up() {\n  &:before {\n    content: \"\\f110\";\n  }\n\n}\n\n.ajs-icon-chevron-up {\n  .ajs-icon-chevron-up();\n}\n\n.ajs-icon-circle() {\n  &:before {\n    content: \"\\f111\";\n  }\n\n}\n\n.ajs-icon-circle {\n  .ajs-icon-circle();\n}\n\n.ajs-icon-cog() {\n  &:before {\n    content: \"\\f112\";\n  }\n\n}\n\n.ajs-icon-cog {\n  .ajs-icon-cog();\n}\n\n.ajs-icon-cogs() {\n  &:before {\n    content: \"\\f113\";\n  }\n\n}\n\n.ajs-icon-cogs {\n  .ajs-icon-cogs();\n}\n\n.ajs-icon-comment() {\n  &:before {\n    content: \"\\f114\";\n  }\n\n}\n\n.ajs-icon-comment {\n  .ajs-icon-comment();\n}\n\n.ajs-icon-compress() {\n  &:before {\n    content: \"\\f115\";\n  }\n\n}\n\n.ajs-icon-compress {\n  .ajs-icon-compress();\n}\n\n.ajs-icon-control-backward() {\n  &:before {\n    content: \"\\f116\";\n  }\n\n}\n\n.ajs-icon-control-backward {\n  .ajs-icon-control-backward();\n}\n\n.ajs-icon-control-fast-forward() {\n  &:before {\n    content: \"\\f117\";\n  }\n\n}\n\n.ajs-icon-control-fast-forward {\n  .ajs-icon-control-fast-forward();\n}\n\n.ajs-icon-control-fast-rewind() {\n  &:before {\n    content: \"\\f118\";\n  }\n\n}\n\n.ajs-icon-control-fast-rewind {\n  .ajs-icon-control-fast-rewind();\n}\n\n.ajs-icon-control-forward() {\n  &:before {\n    content: \"\\f119\";\n  }\n\n}\n\n.ajs-icon-control-forward {\n  .ajs-icon-control-forward();\n}\n\n.ajs-icon-control-pause() {\n  &:before {\n    content: \"\\f11a\";\n  }\n\n}\n\n.ajs-icon-control-pause {\n  .ajs-icon-control-pause();\n}\n\n.ajs-icon-control-play() {\n  &:before {\n    content: \"\\f11b\";\n  }\n\n}\n\n.ajs-icon-control-play {\n  .ajs-icon-control-play();\n}\n\n.ajs-icon-control-rewind() {\n  &:before {\n    content: \"\\f11c\";\n  }\n\n}\n\n.ajs-icon-control-rewind {\n  .ajs-icon-control-rewind();\n}\n\n.ajs-icon-controlbar-compress() {\n  &:before {\n    content: \"\\f11d\";\n  }\n\n}\n\n.ajs-icon-controlbar-compress {\n  .ajs-icon-controlbar-compress();\n}\n\n.ajs-icon-controlbar-fullscreen() {\n  &:before {\n    content: \"\\f11e\";\n  }\n\n}\n\n.ajs-icon-controlbar-fullscreen {\n  .ajs-icon-controlbar-fullscreen();\n}\n\n.ajs-icon-controlbar-pause() {\n  &:before {\n    content: \"\\f11f\";\n  }\n\n}\n\n.ajs-icon-controlbar-pause {\n  .ajs-icon-controlbar-pause();\n}\n\n.ajs-icon-controlbar-play() {\n  &:before {\n    content: \"\\f120\";\n  }\n\n}\n\n.ajs-icon-controlbar-play {\n  .ajs-icon-controlbar-play();\n}\n\n.ajs-icon-controlbar-settings() {\n  &:before {\n    content: \"\\f121\";\n  }\n\n}\n\n.ajs-icon-controlbar-settings {\n  .ajs-icon-controlbar-settings();\n}\n\n.ajs-icon-controlbar-volume-left-off() {\n  &:before {\n    content: \"\\f122\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume-left-off {\n  .ajs-icon-controlbar-volume-left-off();\n}\n\n.ajs-icon-controlbar-volume-left() {\n  &:before {\n    content: \"\\f123\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume-left {\n  .ajs-icon-controlbar-volume-left();\n}\n\n.ajs-icon-controlbar-volume-min() {\n  &:before {\n    content: \"\\f124\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume-min {\n  .ajs-icon-controlbar-volume-min();\n}\n\n.ajs-icon-controlbar-volume-off() {\n  &:before {\n    content: \"\\f125\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume-off {\n  .ajs-icon-controlbar-volume-off();\n}\n\n.ajs-icon-controlbar-volume-right-off() {\n  &:before {\n    content: \"\\f126\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume-right-off {\n  .ajs-icon-controlbar-volume-right-off();\n}\n\n.ajs-icon-controlbar-volume-right() {\n  &:before {\n    content: \"\\f127\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume-right {\n  .ajs-icon-controlbar-volume-right();\n}\n\n.ajs-icon-controlbar-volume_max() {\n  &:before {\n    content: \"\\f128\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume_max {\n  .ajs-icon-controlbar-volume_max();\n}\n\n.ajs-icon-controlbar-volume_middle() {\n  &:before {\n    content: \"\\f129\";\n  }\n\n}\n\n.ajs-icon-controlbar-volume_middle {\n  .ajs-icon-controlbar-volume_middle();\n}\n\n.ajs-icon-download() {\n  &:before {\n    content: \"\\f12a\";\n  }\n\n}\n\n.ajs-icon-download {\n  .ajs-icon-download();\n}\n\n.ajs-icon-eject() {\n  &:before {\n    content: \"\\f12b\";\n  }\n\n}\n\n.ajs-icon-eject {\n  .ajs-icon-eject();\n}\n\n.ajs-icon-ellipsis-h() {\n  &:before {\n    content: \"\\f12c\";\n  }\n\n}\n\n.ajs-icon-ellipsis-h {\n  .ajs-icon-ellipsis-h();\n}\n\n.ajs-icon-ellipsis-v() {\n  &:before {\n    content: \"\\f12d\";\n  }\n\n}\n\n.ajs-icon-ellipsis-v {\n  .ajs-icon-ellipsis-v();\n}\n\n.ajs-icon-eraser() {\n  &:before {\n    content: \"\\f12e\";\n  }\n\n}\n\n.ajs-icon-eraser {\n  .ajs-icon-eraser();\n}\n\n.ajs-icon-expand() {\n  &:before {\n    content: \"\\f12f\";\n  }\n\n}\n\n.ajs-icon-expand {\n  .ajs-icon-expand();\n}\n\n.ajs-icon-eye-off() {\n  &:before {\n    content: \"\\f130\";\n  }\n\n}\n\n.ajs-icon-eye-off {\n  .ajs-icon-eye-off();\n}\n\n.ajs-icon-eye-on() {\n  &:before {\n    content: \"\\f131\";\n  }\n\n}\n\n.ajs-icon-eye-on {\n  .ajs-icon-eye-on();\n}\n\n.ajs-icon-facetime() {\n  &:before {\n    content: \"\\f132\";\n  }\n\n}\n\n.ajs-icon-facetime {\n  .ajs-icon-facetime();\n}\n\n.ajs-icon-female() {\n  &:before {\n    content: \"\\f133\";\n  }\n\n}\n\n.ajs-icon-female {\n  .ajs-icon-female();\n}\n\n.ajs-icon-github() {\n  &:before {\n    content: \"\\f134\";\n  }\n\n}\n\n.ajs-icon-github {\n  .ajs-icon-github();\n}\n\n.ajs-icon-information() {\n  &:before {\n    content: \"\\f135\";\n  }\n\n}\n\n.ajs-icon-information {\n  .ajs-icon-information();\n}\n\n.ajs-icon-jogs-backward-0x() {\n  &:before {\n    content: \"\\f136\";\n  }\n\n}\n\n.ajs-icon-jogs-backward-0x {\n  .ajs-icon-jogs-backward-0x();\n}\n\n.ajs-icon-jogs-backward-1x() {\n  &:before {\n    content: \"\\f137\";\n  }\n\n}\n\n.ajs-icon-jogs-backward-1x {\n  .ajs-icon-jogs-backward-1x();\n}\n\n.ajs-icon-jogs-backward-2x() {\n  &:before {\n    content: \"\\f138\";\n  }\n\n}\n\n.ajs-icon-jogs-backward-2x {\n  .ajs-icon-jogs-backward-2x();\n}\n\n.ajs-icon-jogs-backward-3x() {\n  &:before {\n    content: \"\\f139\";\n  }\n\n}\n\n.ajs-icon-jogs-backward-3x {\n  .ajs-icon-jogs-backward-3x();\n}\n\n.ajs-icon-jogs-backward-4x() {\n  &:before {\n    content: \"\\f13a\";\n  }\n\n}\n\n.ajs-icon-jogs-backward-4x {\n  .ajs-icon-jogs-backward-4x();\n}\n\n.ajs-icon-jogs-center() {\n  &:before {\n    content: \"\\f13b\";\n  }\n\n}\n\n.ajs-icon-jogs-center {\n  .ajs-icon-jogs-center();\n}\n\n.ajs-icon-jogs-fast-backward() {\n  &:before {\n    content: \"\\f13c\";\n  }\n\n}\n\n.ajs-icon-jogs-fast-backward {\n  .ajs-icon-jogs-fast-backward();\n}\n\n.ajs-icon-jogs-fast-forward() {\n  &:before {\n    content: \"\\f13d\";\n  }\n\n}\n\n.ajs-icon-jogs-fast-forward {\n  .ajs-icon-jogs-fast-forward();\n}\n\n.ajs-icon-jogs-forward-0x() {\n  &:before {\n    content: \"\\f13e\";\n  }\n\n}\n\n.ajs-icon-jogs-forward-0x {\n  .ajs-icon-jogs-forward-0x();\n}\n\n.ajs-icon-jogs-forward-1x() {\n  &:before {\n    content: \"\\f13f\";\n  }\n\n}\n\n.ajs-icon-jogs-forward-1x {\n  .ajs-icon-jogs-forward-1x();\n}\n\n.ajs-icon-jogs-forward-2x() {\n  &:before {\n    content: \"\\f140\";\n  }\n\n}\n\n.ajs-icon-jogs-forward-2x {\n  .ajs-icon-jogs-forward-2x();\n}\n\n.ajs-icon-jogs-forward-3x() {\n  &:before {\n    content: \"\\f141\";\n  }\n\n}\n\n.ajs-icon-jogs-forward-3x {\n  .ajs-icon-jogs-forward-3x();\n}\n\n.ajs-icon-jogs-forward-4x() {\n  &:before {\n    content: \"\\f142\";\n  }\n\n}\n\n.ajs-icon-jogs-forward-4x {\n  .ajs-icon-jogs-forward-4x();\n}\n\n.ajs-icon-key() {\n  &:before {\n    content: \"\\f143\";\n  }\n\n}\n\n.ajs-icon-key {\n  .ajs-icon-key();\n}\n\n.ajs-icon-legal() {\n  &:before {\n    content: \"\\f144\";\n  }\n\n}\n\n.ajs-icon-legal {\n  .ajs-icon-legal();\n}\n\n.ajs-icon-list-close() {\n  &:before {\n    content: \"\\f145\";\n  }\n\n}\n\n.ajs-icon-list-close {\n  .ajs-icon-list-close();\n}\n\n.ajs-icon-list-open() {\n  &:before {\n    content: \"\\f146\";\n  }\n\n}\n\n.ajs-icon-list-open {\n  .ajs-icon-list-open();\n}\n\n.ajs-icon-lock-close() {\n  &:before {\n    content: \"\\f147\";\n  }\n\n}\n\n.ajs-icon-lock-close {\n  .ajs-icon-lock-close();\n}\n\n.ajs-icon-lock-open() {\n  &:before {\n    content: \"\\f148\";\n  }\n\n}\n\n.ajs-icon-lock-open {\n  .ajs-icon-lock-open();\n}\n\n.ajs-icon-male() {\n  &:before {\n    content: \"\\f149\";\n  }\n\n}\n\n.ajs-icon-male {\n  .ajs-icon-male();\n}\n\n.ajs-icon-microphone-off() {\n  &:before {\n    content: \"\\f14a\";\n  }\n\n}\n\n.ajs-icon-microphone-off {\n  .ajs-icon-microphone-off();\n}\n\n.ajs-icon-microphone-on() {\n  &:before {\n    content: \"\\f14b\";\n  }\n\n}\n\n.ajs-icon-microphone-on {\n  .ajs-icon-microphone-on();\n}\n\n.ajs-icon-minus() {\n  &:before {\n    content: \"\\f14c\";\n  }\n\n}\n\n.ajs-icon-minus {\n  .ajs-icon-minus();\n}\n\n.ajs-icon-music() {\n  &:before {\n    content: \"\\f14d\";\n  }\n\n}\n\n.ajs-icon-music {\n  .ajs-icon-music();\n}\n\n.ajs-icon-picture() {\n  &:before {\n    content: \"\\f14e\";\n  }\n\n}\n\n.ajs-icon-picture {\n  .ajs-icon-picture();\n}\n\n.ajs-icon-plus() {\n  &:before {\n    content: \"\\f14f\";\n  }\n\n}\n\n.ajs-icon-plus {\n  .ajs-icon-plus();\n}\n\n.ajs-icon-power() {\n  &:before {\n    content: \"\\f150\";\n  }\n\n}\n\n.ajs-icon-power {\n  .ajs-icon-power();\n}\n\n.ajs-icon-refresh() {\n  &:before {\n    content: \"\\f151\";\n  }\n\n}\n\n.ajs-icon-refresh {\n  .ajs-icon-refresh();\n}\n\n.ajs-icon-remove() {\n  &:before {\n    content: \"\\f152\";\n  }\n\n}\n\n.ajs-icon-remove {\n  .ajs-icon-remove();\n}\n\n.ajs-icon-reorder() {\n  &:before {\n    content: \"\\f153\";\n  }\n\n}\n\n.ajs-icon-reorder {\n  .ajs-icon-reorder();\n}\n\n.ajs-icon-screenshot() {\n  &:before {\n    content: \"\\f154\";\n  }\n\n}\n\n.ajs-icon-screenshot {\n  .ajs-icon-screenshot();\n}\n\n.ajs-icon-scrubber-cursor() {\n  &:before {\n    content: \"\\f155\";\n  }\n\n}\n\n.ajs-icon-scrubber-cursor {\n  .ajs-icon-scrubber-cursor();\n}\n\n.ajs-icon-search() {\n  &:before {\n    content: \"\\f156\";\n  }\n\n}\n\n.ajs-icon-search {\n  .ajs-icon-search();\n}\n\n.ajs-icon-sign-in() {\n  &:before {\n    content: \"\\f157\";\n  }\n\n}\n\n.ajs-icon-sign-in {\n  .ajs-icon-sign-in();\n}\n\n.ajs-icon-sign-out() {\n  &:before {\n    content: \"\\f158\";\n  }\n\n}\n\n.ajs-icon-sign-out {\n  .ajs-icon-sign-out();\n}\n\n.ajs-icon-sort() {\n  &:before {\n    content: \"\\f159\";\n  }\n\n}\n\n.ajs-icon-sort {\n  .ajs-icon-sort();\n}\n\n.ajs-icon-sound-link-off() {\n  &:before {\n    content: \"\\f15a\";\n  }\n\n}\n\n.ajs-icon-sound-link-off {\n  .ajs-icon-sound-link-off();\n}\n\n.ajs-icon-sound-link-on() {\n  &:before {\n    content: \"\\f15b\";\n  }\n\n}\n\n.ajs-icon-sound-link-on {\n  .ajs-icon-sound-link-on();\n}\n\n.ajs-icon-stop() {\n  &:before {\n    content: \"\\f15c\";\n  }\n\n}\n\n.ajs-icon-stop {\n  .ajs-icon-stop();\n}\n\n.ajs-icon-transcription() {\n  &:before {\n    content: \"\\f15d\";\n  }\n\n}\n\n.ajs-icon-transcription {\n  .ajs-icon-transcription();\n}\n\n.ajs-icon-volume-down() {\n  &:before {\n    content: \"\\f15e\";\n  }\n\n}\n\n.ajs-icon-volume-down {\n  .ajs-icon-volume-down();\n}\n\n.ajs-icon-volume-off() {\n  &:before {\n    content: \"\\f15f\";\n  }\n\n}\n\n.ajs-icon-volume-off {\n  .ajs-icon-volume-off();\n}\n\n.ajs-icon-volume-up() {\n  &:before {\n    content: \"\\f160\";\n  }\n\n}\n\n.ajs-icon-volume-up {\n  .ajs-icon-volume-up();\n}\n\n.ajs-icon-zoom-in() {\n  &:before {\n    content: \"\\f161\";\n  }\n\n}\n\n.ajs-icon-zoom-in {\n  .ajs-icon-zoom-in();\n}\n\n.ajs-icon-zoom-out() {\n  &:before {\n    content: \"\\f162\";\n  }\n\n}\n\n.ajs-icon-zoom-out {\n  .ajs-icon-zoom-out();\n}\n"
  },
  {
    "path": "src/assets/less/default-style.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n.ajs-tooltip {\r\n  font-family: Arial, Helvetica, sans-serif;\r\n  color: #fff;\r\n  background: #0cf;\r\n  padding: 10px 5px 10px 5px;\r\n  max-width: 300px;\r\n  box-shadow: none;\r\n  border: none;\r\n  margin-top: -50px;\r\n  &:after {\r\n    content: \"\";\r\n    position: absolute;\r\n    left: 50%;\r\n    top: -20px;\r\n    width: 15px;\r\n    height: 15px;\r\n    box-shadow: none;\r\n    -webkit-transform: rotate(45deg);\r\n    -moz-transform: rotate(45deg);\r\n    -ms-transform: rotate(45deg);\r\n    -o-transform: rotate(45deg);\r\n    tranform: rotate(45deg);\r\n    border: none;\r\n    background: #0cf;\r\n    margin-left: -7.5px;\r\n  }\r\n}\r\n\r\n.ui-tooltip {\r\n  padding: 8px;\r\n  position: absolute;\r\n  z-index: 9999;\r\n  max-width: 300px;\r\n  -webkit-box-shadow: 0 0 5px #aaa;\r\n  box-shadow: 0 0 5px #aaa;\r\n}\r\n\r\n.ui-helper-hidden-accessible {\r\n  display: none;\r\n}"
  },
  {
    "path": "src/assets/less/default.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**************************************************\r\n****************** LESS VARIABLES *****************\r\n**************************************************/\r\n/***** FONTS ****/\r\n@fontMain: Arial, Helvetica, sans-serif;\r\n/***** COLORS ****/\r\n@BtToolContColor : #6c6c6c;\r\n\r\n/**************************************************\r\n****************** THEMES PROJETS *****************\r\n**************************************************/\r\n/***** PLAYER PROJET GEN ****/\r\n@mainColor: #0CF;\r\n@mainBackgroundColor: rgba(19, 231, 240, 0.68);\r\n@mainGradient: linear-gradient(to right, #13e7f0, #0c7abf);\r\n@inverseBackgroundColor: rgba(51, 51, 51, 0.68);\r\n/***** PLAYER TEXTE SYNCRO SNOOPDIC ****/\r\n/*\r\n@mainColor: #0CF;\r\n@mainBackgroundColor: rgba(19, 231, 240, 0.68);\r\n@mainGradient: linear-gradient(to right, #13e7f0, #0c7abf);\r\n*/\r\n/***** PLAYER TEXTE SYNCRO ****/\r\n/*\r\n@mainColor: #bf009a;\r\n@mainBackgroundColor: rgba(191, 0, 154, 0.6);\r\n@mainGradient: linear-gradient(to right, #e55ac9, #96047b);\r\n*/\r\n\r\n@import \"ajs-animations\";\r\n@import \"ajs-webfont\";\r\n@import \"default-style\";\r\n\r\n@import \"player\";\r\n@import \"plugin\";\r\n"
  },
  {
    "path": "src/assets/less/player-custom-control-bar.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n* Plugin custom control bar\r\n*/\r\n.ajs .plugin-custom-controlbar {\r\n  width: 100%;\r\n  background-color: #1d1d1d;\r\n  background-image: -webkit-gradient(linear, 100% 0, 0 100%, from(#232323), to(#1d1d1d));\r\n  background-image: -webkit-linear-gradient(top, #232323 0%, #1d1d1d 100%);\r\n  background-image: -moz-linear-gradient(top, #232323 0%, #1d1d1d 100%);\r\n  background-image: -o-linear-gradient(top, #232323 0%, #1d1d1d 100%);\r\n  background-image: linear-gradient(#232323, #1d1d1d);\r\n  overflow: visible;\r\n  margin-bottom: 0px;\r\n  z-index: 900;\r\n  position: relative;\r\n  opacity: .9;\r\n  .ajs-row {\r\n    margin: 0px;\r\n    &:before {\r\n      content: \" \";\r\n      display: table;\r\n    }\r\n    .ajs-container {\r\n      width: 33.33333333%;\r\n      float: left;\r\n      position: relative;\r\n      min-height: 1px;\r\n      padding: 0px;\r\n      margin: 0px;\r\n      height: 110px;\r\n      &.left-container {\r\n      }\r\n      &.middle-container {\r\n      }\r\n      &.right-container {\r\n      }\r\n    }\r\n\r\n  }\r\n  .player-base-button {\r\n    background-color: none;\r\n    line-height: 110px;\r\n    margin: 0 auto;\r\n    text-align: center;\r\n    width: 120px;\r\n    color: #fff;\r\n    float: right;\r\n    cursor: pointer;\r\n  }\r\n  .player-play-button,\r\n  .player-pause-button {\r\n    background-color: none;\r\n    line-height: 110px;\r\n    margin: 0 auto;\r\n    text-align: center;\r\n    width: 120px;\r\n    color: #fff;\r\n\r\n    &.on {\r\n      display: block;\r\n    }\r\n    &.off {\r\n      display: none;\r\n    }\r\n    span.button-container {\r\n      background: #191919;\r\n      border: 1px solid #4d4d4d;\r\n      border-radius: 50%;\r\n      cursor: pointer;\r\n      display: inline-block;\r\n      height: 90px;\r\n      width: 90px;\r\n      line-height: 95px;\r\n      margin: auto;\r\n      position: relative;\r\n      vertical-align: middle;\r\n      padding: 0px;\r\n      font-size: 2em;\r\n      @media (max-width: 668px) {\r\n        line-height: 70px;\r\n        height: 70px;\r\n        width: 70px;\r\n        padding-top: 1px;\r\n        font-size: 1.5em;\r\n      }\r\n      &:hover {\r\n        background: #000;\r\n      }\r\n    }\r\n  }\r\n  .player-label {\r\n    display: inline-block;\r\n    width: 160px;\r\n    line-height: 30px;\r\n    margin-left: 3px;\r\n    margin-right: 3px;\r\n    height: 28px;\r\n    background: none;\r\n  }\r\n  .player-timelabel.time-display {\r\n    display: block;\r\n    height: 100px;\r\n    float: left;\r\n    line-height: 40px;\r\n    padding: 25px 0 0 10px;\r\n    .time-current {\r\n      color: white;\r\n      display: block;\r\n      font-size: 2.5em;\r\n      @media (max-width: 668px) {\r\n        font-size: 1.5em;\r\n      }\r\n    }\r\n    .time-separator {\r\n      display: block;\r\n      height: 1px;\r\n    }\r\n    .time-duration {\r\n      color: white;\r\n      display: block;\r\n      text-align: right;\r\n    }\r\n  }\r\n  .player-fullscreen-button {\r\n    width: 50px;\r\n    display: inline-block;\r\n    vertical-align: middle;\r\n    text-align: center;\r\n    line-height: 100px;\r\n    color: #fff;\r\n    margin: 10px 0 0 20px;\r\n    cursor: pointer;\r\n    width: 50px;\r\n    display: inline-block;\r\n    vertical-align: middle;\r\n    text-align: center;\r\n    line-height: 100px;\r\n    color: #fff;\r\n    margin: 10px 0 0 20px;\r\n    cursor: pointer;\r\n    float: right;\r\n    font-size: 2.4em;\r\n  }\r\n  .player-volume-control {\r\n    vertical-align: middle;\r\n    padding-top: 25px;\r\n    cursor: pointer;\r\n    float: right;\r\n    font-size: 2em;\r\n    @media (max-width: 567px) {\r\n      display: none;\r\n    }\r\n    .volume-control-btn {\r\n      position: absolute;\r\n      top: 0;\r\n      margin-left: 22px;\r\n      margin-top: 48px;\r\n      color: #FFF;\r\n      height: 50px;\r\n      /*width: 30px;*/\r\n      line-height: 30px;\r\n      display: block;\r\n      /*bottom: -51px;*/\r\n      text-align: center;\r\n      cursor: pointer;\r\n    }\r\n  }\r\n  .player-progress-bar {\r\n    height: 20px;\r\n    margin-bottom: 0px;\r\n    overflow: hidden;\r\n    background-color: #6a6969;\r\n    border-radius: 0px;\r\n    clear: both;\r\n    position: relative;\r\n    text-align: left;\r\n    color: #404040;\r\n    .buffer-bar {\r\n      display: block;\r\n      height: 100%;\r\n      background-color: #555;\r\n      position: relative;\r\n      top: 0px;\r\n      box-sizing: border-box;\r\n      width: 0px;\r\n    }\r\n    .ui-slider-range {\r\n      float: left;\r\n      width: 0;\r\n      height: 100%;\r\n      font-size: 12px;\r\n      line-height: 20px;\r\n      color: #fff;\r\n      text-align: center;\r\n      background-color: #58c8b0;\r\n      position: absolute;\r\n      z-index: 1;\r\n      display: block;\r\n      border: 0;\r\n      background-position: 0 0;\r\n    }\r\n\r\n    .ui-slider-handle {\r\n      background-color: #fff;\r\n      border-radius: 0%;\r\n      cursor: pointer;\r\n      float: right;\r\n      height: 25px;\r\n      right: 0;\r\n      width: 25px;\r\n      z-index: 2;\r\n      text-shadow: none;\r\n      top: -.3em;\r\n      margin-left: -.6em;\r\n      position: absolute;\r\n      background-color: #e6e6e6;\r\n      background-repeat: no-repeat;\r\n      background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));\r\n      background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\r\n      background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);\r\n      background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\r\n      background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\r\n      background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\r\n      filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);\r\n      text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);\r\n      color: #333;\r\n      font-size: 13px;\r\n      line-height: normal;\r\n      border: 1px solid #ccc;\r\n      border-bottom-color: #bbb;\r\n      -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\r\n      -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\r\n      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\r\n      -webkit-transition: 0.1s linear background-image;\r\n      -moz-transition: 0.1s linear background-image;\r\n      -ms-transition: 0.1s linear background-image;\r\n      -o-transition: 0.1s linear background-image;\r\n      transition: 0.1s linear background-image;\r\n      overflow: visible;\r\n    }\r\n    &.ui-slider .ui-slider-range {\r\n      background: @mainGradient;\r\n      float: left;\r\n      width: 0;\r\n      height: 100%;\r\n      font-size: 12px;\r\n      line-height: 20px;\r\n      color: #fff;\r\n      text-align: center;\r\n      left: 0;\r\n      top: 0;\r\n      position: absolute;\r\n      z-index: 1;\r\n      display: block;\r\n      border: 0;\r\n      background-position: 0 0;\r\n      color: #ffffff;\r\n      background-color: #0064cd;\r\n      background-repeat: repeat-x;\r\n      text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\r\n      border-color: #0064cd #0064cd #003f81;\r\n      border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\r\n    }\r\n    &.ui-widget-content {\r\n      background: #6c6c6c;\r\n      border: none;\r\n    }\r\n  }\r\n  .player-channel-volume-control {\r\n    padding-top: 0;\r\n    cursor: pointer;\r\n    float: left;\r\n    height: 35px;\r\n    width: 35px;\r\n    line-height: 30px;\r\n    color: #fff;\r\n    .volume-control-btn {\r\n      vertical-align: middle;\r\n      padding-top: 15px;\r\n      top: 0;\r\n      margin-left: 54px;\r\n      margin-top: 8px;\r\n      height: 35px;\r\n      line-height: 30px;\r\n      display: block;\r\n      font-size: 35px;\r\n    }\r\n\r\n    .channel-volume-sliders {\r\n      position: absolute;\r\n      bottom: 10px;\r\n      padding-left: 0px;\r\n      height: 211px;\r\n      background-repeat: no-repeat;\r\n      left: 24px;\r\n      display: none;\r\n      background: linear-gradient(90deg, #666666 50%, #797979 50%);\r\n      &.on {\r\n        display: block;\r\n      }\r\n      .channel-volume-info {\r\n        display: block;\r\n        position: relative;\r\n        padding-left: 5px;\r\n        .channel-volume-info-left,\r\n        .channel-volume-info-right {\r\n          width: 38px;\r\n          height: 40px;\r\n          color: #fff;\r\n          float: left;\r\n          text-align: center;\r\n          margin-left: 0px;\r\n          font-size: 25px;\r\n          padding-left: 2px;\r\n        }\r\n\r\n        .channel-volume-info-left {\r\n          text-align: left;\r\n        }\r\n        .channel-volume-info-right {\r\n          text-align: center;\r\n        }\r\n        .channel-volume-info-mid {\r\n          width: 12px;\r\n          height: 40px;\r\n          color: #fff;\r\n          float: left;\r\n          text-align: center;\r\n          padding-top: 10px;\r\n          left: 30px;\r\n          margin-left: 0px;\r\n          .unify {\r\n            color: #EEE;\r\n            font-size: 23px;\r\n            font-weight: bold;\r\n            margin: -10px;\r\n            &.on {\r\n              color: cadetblue;\r\n            }\r\n          }\r\n        }\r\n      }\r\n      .channel-volume-control {\r\n        display: block;\r\n        margin: 0px;\r\n\r\n        .channel-volume-control-left,\r\n        .channel-volume-control-right {\r\n          height: 145px;\r\n          color: #fff;\r\n          float: left;\r\n          width: 30px;\r\n          margin-left: 19px;\r\n\r\n          .ui-state-focus,\r\n          .ui-state-hover {\r\n            background-color: cadetblue;\r\n          }\r\n          .ui-slider .ui-slider-handle {\r\n            position: absolute;\r\n            z-index: 2;\r\n            width: 18px;\r\n            height: 18px;\r\n            cursor: default;\r\n            -ms-touch-action: none;\r\n            touch-action: none;\r\n            border-radius: 100%;\r\n          }\r\n          .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {\r\n            border: 1px solid #999999;\r\n            background: #999999;\r\n            font-weight: normal;\r\n            color: #454545;\r\n          }\r\n          .ui-slider-vertical {\r\n            width: 4px;\r\n            height: 140px;\r\n          }\r\n          .ui-widget-content {\r\n            border: 1px solid #FFF;\r\n            background: #fff;\r\n            color: #333;\r\n          }\r\n          .ui-slider-vertical .ui-slider-handle {\r\n            left: -8px;\r\n            margin-left: 0;\r\n            margin-bottom: -.6em;\r\n          }\r\n        }\r\n        .channel-volume-control-mid {\r\n          width: 35px;\r\n          height: 3px;\r\n          background-color: #999;\r\n          clear: both;\r\n          background-repeat: no-repeat;\r\n          margin-left: 5px;\r\n          position: absolute;\r\n          visibility: hidden;\r\n          left: 24px;\r\n          top: 49px;\r\n          &.on {\r\n            visibility: visible;\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n  .player-jog-shuttle-button {\r\n    line-height: 30px;\r\n    color: #fff;\r\n    float: left;\r\n    display: block;\r\n    width: 240px;\r\n    position: relative;\r\n    border: 1px solid #cacaca;\r\n    border-radius: 20px;\r\n    height: 38px;\r\n    top: 18px;\r\n    span.button-container {\r\n      border: none;\r\n      border-radius: 0%;\r\n      display: inline-block;\r\n      height: 32px;\r\n      line-height: 40px;\r\n      font-size: 35px;\r\n      width: 100%;\r\n      position: relative;\r\n      top: 2px;\r\n\r\n      .jog-shuttle-separator {\r\n        display: inline-block;\r\n        width: 2px;\r\n        background-color: #eee;\r\n        height: 32px;\r\n      }\r\n      .backward-container,\r\n      .forward-container {\r\n        height: 32px;\r\n        display: inline-block;\r\n        line-height: 30px;\r\n        float: none;\r\n        color: rgba(238, 238, 238, 0.25);\r\n        margin-top: 4px;\r\n        padding-right: 5px;\r\n        padding-left: 5px;\r\n        span {\r\n          height: 32px;\r\n          padding-left: 10px;\r\n          padding-right: 10px;\r\n          position: absolute;\r\n          color: #66cc99;\r\n          &.ajs-icon-jogs-forward-0x,\r\n          &.ajs-icon-jogs-backward-0x {\r\n            visibility: hidden;\r\n          }\r\n          &.ajs-icon-jogs-backward-1x,\r\n          &.ajs-icon-jogs-backward-2x,\r\n          &.ajs-icon-jogs-backward-3x,\r\n          &.ajs-icon-jogs-backward-4x,\r\n          &.ajs-icon-jogs-fast-backward {\r\n            left: 0;\r\n          }\r\n\r\n          &.ajs-icon-jogs-forward-1x,\r\n          &.ajs-icon-jogs-forward-2x,\r\n          &.ajs-icon-jogs-forward-3x,\r\n          &.ajs-icon-jogs-forward-4x,\r\n          &.ajs-icon-jogs-fast-forward {\r\n            color: #66cc99;\r\n            right: -5px;\r\n            top: 1px;\r\n          }\r\n          &.ajs-icon-jogs-backward-1x,\r\n          &.ajs-icon-jogs-backward-2x,\r\n          &.ajs-icon-jogs-backward-3x,\r\n          &.ajs-icon-jogs-backward-4x,\r\n          &.ajs-icon-jogs-fast-backward {\r\n            color: #66cc99;\r\n            left: -5px;\r\n            top: 1px;\r\n          }\r\n        }\r\n      }\r\n      .backward-container {\r\n        float: left;\r\n        text-align: left;\r\n        border: none;\r\n        width: 74px;\r\n      }\r\n      .forward-container {\r\n        float: right;\r\n        text-align: right;\r\n        width: 74px;\r\n      }\r\n      .jog-shuttle {\r\n        background-color: transparent;\r\n        border-radius: 20px;\r\n        position: absolute;\r\n        width: 151px;\r\n        top: 0px;\r\n        height: 32px;\r\n        margin-left: 43px;\r\n        -webkit-transition: opacity 0.5s ease-in;\r\n        -moz-transition: opacity 0.5s ease-in;\r\n        -o-transition: opacity 0.5s ease-in;\r\n        -ms-transition: opacity 0.5s ease-in;\r\n        transition: opacity 0.5s ease-in;\r\n        display: none;\r\n        border: none;\r\n        &.on {\r\n          display: block;\r\n        }\r\n        span.ui-slider-handle {\r\n          border: 1px solid;\r\n          top: -8px;\r\n          margin: 0;\r\n          position: absolute;\r\n          z-index: 2;\r\n          cursor: pointer;\r\n          padding: 0;\r\n          border-radius: 100%;\r\n          background-color: #1D1D1D;\r\n          height: 48px;\r\n          width: 50px;\r\n          line-height: 30px;\r\n          margin-left: -25px;\r\n          font-size: 48px;\r\n          color: #eee;\r\n          font-weight: normal;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  &.sticky {\r\n    background-image: none;\r\n    opacity: 1;\r\n    .ajs-row {\r\n\r\n      .ajs-container {\r\n        height: 35px;\r\n        &.left-container {\r\n\r\n        }\r\n        &.middle-container {\r\n          text-align: center;\r\n        }\r\n        &.right-container {\r\n        }\r\n      }\r\n    }\r\n    .player-base-button {\r\n      line-height: 25px;\r\n      width: 35px;\r\n      height: 35px;\r\n      vertical-align: middle;\r\n      text-align: center;\r\n      padding-top: 5px;\r\n      float: none;\r\n      display: inline-block;\r\n      font-size: 1.4em;\r\n\r\n    }\r\n    .player-play-button,\r\n    .player-pause-button {\r\n      line-height: 30px;\r\n      width: 35px;\r\n      color: #fff;\r\n      display: block;\r\n      &.on {\r\n        display: inline-block;\r\n      }\r\n      &.off {\r\n        display: none;\r\n      }\r\n      span.button-container {\r\n        border: none;\r\n        border-radius: 0%;\r\n        display: inline-block;\r\n        height: 35px;\r\n        width: 35px;\r\n        line-height: 40px;\r\n        font-size: 35px;\r\n      }\r\n    }\r\n\r\n    .player-timelabel.time-display {\r\n      display: inline-block;\r\n      line-height: 35px;\r\n      padding: 0px 0px 0px 10px;\r\n      height: 35px;\r\n      .time-current {\r\n        color: white;\r\n        display: inline-block;\r\n        font-size: 1em;\r\n        @media (max-width: 567px) {\r\n          font-size: 0.8em;\r\n        }\r\n      }\r\n      .time-separator {\r\n        display: inline-block;\r\n        color: white;\r\n        padding-left: 5px;\r\n        padding-right: 5px;\r\n        font-size: 0.9em;\r\n        &:before {\r\n          content: \"/\";\r\n        }\r\n        @media (max-width: 567px) {\r\n          display: none;\r\n        }\r\n      }\r\n      .time-duration {\r\n        display: inline-block;\r\n        font-size: 0.9em;\r\n        @media (max-width: 567px) {\r\n          display: none;\r\n        }\r\n      }\r\n    }\r\n    .player-timelabel.time-display.off {\r\n      .time-separator {\r\n        display: none;\r\n      }\r\n    }\r\n    .player-fullscreen-button {\r\n      display: inline-block;\r\n      color: #fff;\r\n      cursor: pointer;\r\n      width: 35px;\r\n      line-height: 18px;\r\n      margin: 5px 10px 0px 35px;\r\n      float: right;\r\n      font-size: 2.4em;\r\n    }\r\n    .player-volume-control {\r\n      padding-top: 0;\r\n      cursor: pointer;\r\n      float: right;\r\n      height: 35px;\r\n      width: 35px;\r\n      line-height: 25px;\r\n\r\n      canvas {\r\n        display: none\r\n      }\r\n      .volume-control-btn {\r\n        top: 0;\r\n        margin-left: 22px;\r\n        margin-top: 5px;\r\n        height: 35px;\r\n        line-height: 30px;\r\n        display: block;\r\n        font-size: 1.4em;\r\n      }\r\n    }\r\n    .player-progress-bar {\r\n      height: 10px;\r\n      .buffer-bar {\r\n      }\r\n      .ui-slider-range {\r\n        line-height: 20px;\r\n      }\r\n\r\n      .ui-slider-handle {\r\n        height: 15px;\r\n        width: 10px;\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "src/assets/less/player.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Player\r\n */\r\n/**\r\n * Plugin control bar\r\n */\r\n.ajs {\r\n  overflow: hidden;\r\n  font-family: @fontMain;\r\n  -webkit-touch-callout: none;\r\n  -webkit-user-select: none;\r\n  -khtml-user-select: none;\r\n  -moz-user-select: none;\r\n  -ms-user-select: none;\r\n  user-select: none;\r\n  line-height: 0px;\r\n\r\n  .ajs-loader {\r\n    color: white;\r\n    position: absolute;\r\n    z-index: 100;\r\n    text-align: center;\r\n    width: 100%;\r\n    height: 100%;\r\n    top: 50%;\r\n  }\r\n\r\n  .ajs-error {\r\n    position: absolute;\r\n    z-index: 100;\r\n    width: 100%;\r\n    height: 100%;\r\n    background-color: black;\r\n    color: white;\r\n    vertical-align: middle;\r\n    text-align: center;\r\n    line-height: 20px;\r\n    top: 0;\r\n    p {\r\n      margin-left: auto;\r\n      margin-right: auto;\r\n      position: relative;\r\n      top: 50%;\r\n      margin-top: -25px;\r\n    }\r\n  }\r\n  /* Reset background with font Awesome */\r\n  .ajs .ui-state-default .ui-icon[class*=\" fa-\"] {\r\n    background: none;\r\n    text-indent: 0;\r\n  }\r\n}\r\n\r\n/**tooltip**/\r\n.ui-tooltip {\r\n  font-family: @fontMain;\r\n  padding: 5px 5px;\r\n  color: white;\r\n  border: none;\r\n  box-shadow: none;\r\n  border: none;\r\n  box-shadow: none;\r\n  background: @mainColor;\r\n  p {\r\n    font-size: 14px;\r\n    margin: 0;\r\n  }\r\n  .timeline-images-component {\r\n    padding: 3px;\r\n    &.tooltip-image {\r\n      width: 200px;\r\n    }\r\n  }\r\n}\r\n\r\n.ajs-arrow {\r\n  width: 70px;\r\n  height: 16px;\r\n  overflow: hidden;\r\n  position: absolute;\r\n  left: 50%;\r\n  margin-left: -35px;\r\n  bottom: -16px;\r\n  border: none;\r\n  &:after {\r\n    content: \"\";\r\n    position: absolute;\r\n    left: 20px;\r\n    top: -20px;\r\n    width: 25px;\r\n    height: 25px;\r\n    box-shadow: none;\r\n    -webkit-transform: rotate(45deg);\r\n    -moz-transform: rotate(45deg);\r\n    -ms-transform: rotate(45deg);\r\n    -o-transform: rotate(45deg);\r\n    tranform: rotate(45deg);\r\n    border: none;\r\n    background: @mainColor;\r\n  }\r\n\r\n  &.top {\r\n    top: -16px;\r\n    bottom: auto;\r\n    &:after {\r\n      bottom: -20px;\r\n      top: auto;\r\n    }\r\n  }\r\n  &.left {\r\n    left: 20%;\r\n  }\r\n}\r\n\r\n.inalabplayer-ui-resizable-helper {\r\n  z-index: 9999;\r\n  border: 1px dashed #6c6c6c !important;\r\n}"
  },
  {
    "path": "src/assets/less/plugin-captions.less",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Plugin caption\n */\n.ajs-plugin.plugin-caption {\n  display: block;\n  color: #FFF;\n  position: absolute;\n  bottom: 65px;\n  line-height: 20px;\n  text-align: center;\n  width: 100%;\n  background-color: @inverseBackgroundColor;\n  min-height: 20px;\n  z-index: 98;\n  padding-left: 0;\n  padding-right: 0;\n  font-size: 1.4em;\n  &.fullScreenOn {\n    font-size: 3em;\n    line-height: 45px;\n  }\n  &.fullScreenOff {\n    font-size: 1.4em;\n  }\n}"
  },
  {
    "path": "src/assets/less/plugin-editor.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n.ajs-plugin.editor {\r\n  position: relative;\r\n  .loader {\r\n    position: absolute;\r\n    top: 110px;\r\n    z-index: 100;\r\n    left: 0;\r\n    right: 0;\r\n    margin: 0;\r\n    padding: 0;\r\n  }\r\n  /**\r\n   * Plugin list editor\r\n   */\r\n  &.plugin-list-editor {\r\n    color: #666;\r\n    .heading {\r\n      height: 30px;\r\n      border: none;\r\n      line-height: 25px;\r\n      background-color: #616161;\r\n      color: #CCC;\r\n      .title {\r\n        text-align: left;\r\n        padding-left: 10px;\r\n        margin: 0;\r\n        color: #FFF;\r\n        line-height: 30px;\r\n        font-size: 14px;\r\n      }\r\n    }\r\n    .body {\r\n      ul.listOfmetadata {\r\n        box-sizing: border-box;\r\n        color: #cacaca;\r\n        display: block;\r\n        font-size: 14px;\r\n        line-height: 20px;\r\n        list-style-type: disc;\r\n        margin-bottom: 0px;\r\n        margin-top: 0px;\r\n        padding-left: 0px;\r\n        height: 90px;\r\n        overflow-y: overlay;\r\n        li.item {\r\n          position: relative;\r\n          display: block;\r\n          padding: 5px 15px;\r\n          margin: 0;\r\n          background-color: #eceff1;\r\n          border: none;\r\n          color: #000;\r\n          font-size: 11px;\r\n          &.selected {\r\n            background-color: #546e7a;\r\n            color: rgba(255, 255, 255, 0.87);\r\n          }\r\n          span.delete {\r\n            float: right;\r\n            padding: 0px;\r\n            font-size: 10px;\r\n            color: #fff;\r\n            text-align: center;\r\n            white-space: nowrap;\r\n            background-color: #E57373;\r\n            border: none;\r\n            border-radius: 50%;\r\n            cursor: pointer;\r\n            display: inline-block;\r\n            height: 20px;\r\n            width: 20px;\r\n            margin: auto;\r\n            position: relative;\r\n            vertical-align: middle;\r\n            box-shadow: none;\r\n            line-height: 23px;\r\n            margin-left: 5px;\r\n            &:before {\r\n              margin-left: -1px;\r\n            }\r\n          }\r\n          span.duplicate {\r\n            float: right;\r\n            padding: 0px;\r\n            font-size: 10px;\r\n            color: #fff;\r\n            text-align: center;\r\n            white-space: nowrap;\r\n            background-color: #3cf;\r\n            border: none;\r\n            border-radius: 50%;\r\n            cursor: pointer;\r\n            display: inline-block;\r\n            height: 20px;\r\n            width: 20px;\r\n            margin: auto;\r\n            position: relative;\r\n            vertical-align: middle;\r\n            box-shadow: none;\r\n            line-height: 23px;\r\n            &:before {\r\n              margin-left: -1px;\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n    .footer {\r\n      height: 25px;\r\n      border: none;\r\n      line-height: 25px;\r\n      background-color: #616161;\r\n      color: #CCC;\r\n      text-align: center;\r\n\r\n      .add-metadata {\r\n        min-width: 10px;\r\n        font-size: 25px;\r\n        font-weight: normal;\r\n        color: #fff;\r\n        text-align: center;\r\n        white-space: nowrap;\r\n        background-color: #00CC99;\r\n        border: none;\r\n        border-radius: 50%;\r\n        cursor: pointer;\r\n        display: inline-block;\r\n        height: 35px;\r\n        width: 35px;\r\n        margin: auto;\r\n        position: relative;\r\n        vertical-align: middle;\r\n        margin-top: -25px;\r\n        box-shadow: none;\r\n        line-height: 40px;\r\n        text-align: left;\r\n        &:before {\r\n          margin-left: -1px;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Plugin block editor\r\n   */\r\n  &.plugin-block-editor {\r\n    color: #666;\r\n    .heading {\r\n      height: 30px;\r\n      border: none;\r\n      line-height: 25px;\r\n      background-color: #616161;\r\n      color: #CCC;\r\n      .title {\r\n        text-align: left;\r\n        padding-left: 10px;\r\n        margin: 0;\r\n        color: #FFF;\r\n        line-height: 30px;\r\n        font-size: 14px;\r\n      }\r\n    }\r\n    .body {\r\n      padding: 5px 5px 15px 5px;\r\n      form.formMetadataBlock {\r\n        .form-item {\r\n          padding: 0px;\r\n          margin: 0px;\r\n          box-shadow: none;\r\n          font-size: 12px;\r\n          .input {\r\n            width: 100%;\r\n            border: none;\r\n            box-shadow: none;\r\n            background-color: #eaeaea;\r\n            color: #000;\r\n            height: 25px;\r\n            padding-left: 5px;\r\n            border-radius: 0px;\r\n            font-size: 12px;\r\n          }\r\n          .text-area {\r\n            width: 100%;\r\n            height: 50px;\r\n            border-radius: 3px;\r\n            border: 1px solid #ccc;\r\n            display: inline-block;\r\n            padding: 4px;\r\n            font-size: 12px;\r\n          }\r\n          .select {\r\n            width: 100%;\r\n            border: none;\r\n            box-shadow: none;\r\n            background-color: #eaeaea;\r\n            color: #000;\r\n            height: 25px;\r\n            padding-left: 5px;\r\n            border-radius: 0px;\r\n            font-size: 12px;\r\n          }\r\n          .shape.ajs-icon {\r\n            font-size: 20px;\r\n          }\r\n        }\r\n      }\r\n      .messages-container {\r\n        padding: 0px;\r\n        margin-bottom: 0px;\r\n        border: none;\r\n        border-radius: 4px;\r\n        background-color: #E1F5FE;\r\n        margin-top: 0px;\r\n        text-align: center;\r\n        &.type-info {\r\n          background-color: #E1F5FE;\r\n        }\r\n        &.type-error {\r\n          background-color: #EF9A9A;\r\n        }\r\n        p.info {\r\n          padding: 0px;\r\n          margin: 0px;\r\n        }\r\n        p.error {\r\n          padding: 0px;\r\n          margin: 0px;\r\n        }\r\n      }\r\n    }\r\n    .footer {\r\n      height: 25px;\r\n      border: none;\r\n      line-height: 25px;\r\n      background-color: #616161;\r\n      color: #CCC;\r\n      text-align: center;\r\n      .save-metadata {\r\n        min-width: 10px;\r\n        padding: 0;\r\n        font-size: 25px;\r\n        font-weight: normal;\r\n        color: #fff;\r\n        text-align: center;\r\n        white-space: nowrap;\r\n        background-color: #03A9F4;\r\n        border: none;\r\n        border-radius: 50%;\r\n        cursor: pointer;\r\n        display: inline-block;\r\n        height: 35px;\r\n        width: 35px;\r\n        margin: auto;\r\n        position: relative;\r\n        vertical-align: middle;\r\n        margin-top: -25px;\r\n        box-shadow: none;\r\n        line-height: 40px;\r\n        &:before {\r\n          margin-left: -1px;\r\n        }\r\n      }\r\n    }\r\n  }\r\n  /**\r\n   * Plugin items editor\r\n   */\r\n  &.plugin-items-editor {\r\n    color: #616161;\r\n    .heading {\r\n      height: 30px;\r\n      border: none;\r\n      line-height: 25px;\r\n      background-color: #616161;\r\n      color: #CCC;\r\n      .title {\r\n        text-align: left;\r\n        padding-left: 10px;\r\n        margin: 0;\r\n        color: #FFF;\r\n        line-height: 30px;\r\n        font-size: 14px;\r\n      }\r\n\r\n    }\r\n    .body {\r\n      .messages-container {\r\n        padding: 5px 0px 5px 0px;\r\n        margin-bottom: 11px;\r\n        border: none;\r\n        border-radius: 4px;\r\n        background-color: #E1F5FE;\r\n        margin-top: 10px;\r\n        text-align: center;\r\n        min-height: 25px;\r\n        &.type-info {\r\n          background-color: #E1F5FE;\r\n        }\r\n        &.type-error {\r\n          background-color: #EF9A9A;\r\n        }\r\n        p.info {\r\n          padding: 0px;\r\n          margin: 0px;\r\n        }\r\n        p.error {\r\n          padding: 0px;\r\n          margin: 0px;\r\n        }\r\n      }\r\n      ul.listOfSelectedItems {\r\n        box-sizing: border-box;\r\n        color: #cacaca;\r\n        display: block;\r\n        font-size: 14px;\r\n        line-height: 20px;\r\n        list-style-type: disc;\r\n        margin-bottom: 0px;\r\n        margin-top: 0px;\r\n        padding-left: 0px;\r\n        height: 285px;\r\n        overflow-y: overlay;\r\n        li.item {\r\n          position: relative;\r\n          display: block;\r\n          padding: 0px;\r\n          margin-bottom: 0;\r\n          background-color: transparent;\r\n          border: none;\r\n          color: #000;\r\n          box-shadow: none;\r\n          font-size: 12px;\r\n          &.error {\r\n            border: 1px solid red;\r\n            text-align: left;\r\n          }\r\n          .form-data {\r\n            padding: 25px 0px 10px 0px;\r\n            border: none;\r\n            background-color: #eceff1;\r\n            margin-right: 0;\r\n            min-height: 56px;\r\n            box-shadow: none;\r\n            margin: 1px 0px 0px 0px;\r\n            .form-input {\r\n              margin: 0px 5px;\r\n              box-shadow: none;\r\n              min-height: 25px;\r\n              .input {\r\n                width: 100%;\r\n                border: none;\r\n                box-shadow: none;\r\n                background-color: #eaeaea;\r\n                color: #000;\r\n                height: 25px;\r\n                padding-left: 5px;\r\n                border-radius: 5px;\r\n                font-size: 12px;\r\n              }\r\n\r\n            }\r\n            .form-controls {\r\n              position: absolute;\r\n              width: auto;\r\n              height: 25px;\r\n              top: 5px;\r\n              right: 10px;\r\n              padding: 0px;\r\n              span.button {\r\n                padding: 0px;\r\n                font-size: 12px;\r\n                font-weight: normal;\r\n                text-align: center;\r\n                white-space: nowrap;\r\n                border: none;\r\n                border-radius: 50%;\r\n                cursor: pointer;\r\n                display: inline-block;\r\n                height: 20px;\r\n                width: 20px;\r\n                margin: 0px 0px 0px 5px;\r\n                position: relative;\r\n                vertical-align: middle;\r\n                padding-top: 0px;\r\n                margin-top: 0;\r\n                top: 0px;\r\n                right: 0px;\r\n                color: white;\r\n                box-shadow: none;\r\n                line-height: 23px;\r\n                opacity: 1;\r\n                filter: none;\r\n                float: none;\r\n                &:before {\r\n                  margin-left: -1px;\r\n                }\r\n                &.valid {\r\n                  background-color: #CACACA;\r\n                  visibility: hidden;\r\n                  &.valid.on {\r\n                    visibility: visible;\r\n                    background-color: #03A9F4;\r\n                  }\r\n                }\r\n                &.remove {\r\n                  background-color: #FFC107;\r\n                }\r\n                &.close {\r\n                  background-color: #757575;\r\n                }\r\n\r\n              }\r\n            }\r\n          }\r\n\r\n        }\r\n      }\r\n\r\n    }\r\n    .footer {\r\n      height: 25px;\r\n      border: none;\r\n      line-height: 25px;\r\n      background-color: #616161;\r\n      color: #CCC;\r\n      text-align: center;\r\n      .button {\r\n        min-width: 10px;\r\n        font-size: 25px;\r\n        font-weight: normal;\r\n        color: #fff;\r\n        text-align: center;\r\n        white-space: nowrap;\r\n        border: none;\r\n        border-radius: 50%;\r\n        cursor: pointer;\r\n        display: inline-block;\r\n        height: 35px;\r\n        width: 35px;\r\n        margin: 5px;\r\n        position: relative;\r\n        vertical-align: middle;\r\n        margin-top: -25px;\r\n        box-shadow: none;\r\n        line-height: 40px;\r\n        &.add {\r\n          background-color: #00CC99;\r\n          &:before {\r\n            margin-left: -1px;\r\n          }\r\n        }\r\n        &.validateAll {\r\n          background-color: #03A9F4;\r\n          &:before {\r\n            margin-left: -1px;\r\n          }\r\n        }\r\n        &.clear {\r\n          background-color: #757575;\r\n          &:before {\r\n            margin-left: -1px;\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "src/assets/less/plugin-menu-contextuel.less",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/** Plugin menu contextuel**/\n.ajs .plugin-context-menu.dropdown-menu {\n  width: 300px;\n  padding: 0px 0px 0px 10px;\n  margin: 0;\n  text-align: left;\n  list-style: none;\n  background-color: #fff;\n  -webkit-background-clip: padding-box;\n  background-clip: padding-box;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, .15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n  box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n  z-index: 1000;\n  &:before {\n    bottom: -14px;\n    border-top: 7px solid #555;\n  }\n  &:before, &:after, li:first-child:after {\n    content: '';\n    display: block;\n    position: absolute;\n    left: 15px;\n    width: 0;\n    height: 0;\n    border: 7px outset transparent;\n  }\n  li {\n    color: #838ca2;\n    text-shadow: none;\n    margin: 0;\n    padding: 0px 0px 0px 25px;\n    border: 0;\n    font-size: 100%;\n    font: inherit;\n    vertical-align: middle;\n    height: 30px;\n    list-style: none;\n    line-height: 30px;\n    display: block;\n    position: relative;\n\n    .ajs-icon {\n      position: absolute;\n      left: 0px;\n      top: 3px;\n      font-size: 24px;\n      line-height: 28px;\n    }\n    &.disabled {\n      a {\n        cursor: default;\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/assets/less/plugin-overlay.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Plugin overlay\r\n */\r\n.ajs-plugin {\r\n  &.plugin-overlay {\r\n    display: block;\r\n    color: #FFF;\r\n    position: absolute;\r\n    width: 100%;\r\n    height: 100%;\r\n    z-index: 98;\r\n    top: 0;\r\n    &.eraser {\r\n      cursor: pointer;\r\n    }\r\n\r\n    .overlay-canvas {\r\n      /*border: 1px solid red;*/\r\n    }\r\n    .error {\r\n      position: absolute;\r\n      top: 9px;\r\n      left: 0;\r\n      width: 100%;\r\n      line-height: 20px;\r\n      color: white;\r\n      font-size: 20px;\r\n      background-color: red;\r\n      font-weight: bold;\r\n    }\r\n    &.draw {\r\n      cursor: crosshair;\r\n    }\r\n    .toolbox {\r\n      width: 100px;\r\n      position: absolute;\r\n      top: 0;\r\n      right: 0;\r\n      border: none;\r\n      padding: 0;\r\n      margin: 0;\r\n      cursor: move;\r\n      opacity: 0.9;\r\n      .add {\r\n        .add-ctrl,\r\n        .close-box,\r\n        .add-shape-msg,\r\n        .add-shape-rectangle,\r\n        .add-shape-ellipse {\r\n          height: 65px;\r\n          width: 101px;\r\n          background-color: #616161;\r\n          color: white;\r\n          font-size: 12px;\r\n          line-height: 20px;\r\n          text-align: center;\r\n          box-shadow: 1px 1px 0px rgb(121, 121, 121);\r\n          &.on {\r\n            background-color: #99CC00;\r\n          }\r\n        }\r\n        .add-shape-rectangle,\r\n        .add-shape-ellipse {\r\n          font-size: 50px;\r\n          line-height: 75px;\r\n        }\r\n        .add-ctrl {\r\n          background-color: #616161;\r\n          .ctrl {\r\n            background-color: #3cf;\r\n          }\r\n        }\r\n        .close-box {\r\n          background-color: #616161;\r\n          .ctrl {\r\n            background-color: transparent;\r\n            border: 3px solid white;\r\n          }\r\n        }\r\n      }\r\n      .erase-box {\r\n        height: 65px;\r\n        width: 101px;\r\n        background-color: rgba(49, 49, 49, 0.7);\r\n        color: white;\r\n        font-size: 12px;\r\n        line-height: 20px;\r\n        text-align: center;\r\n        box-shadow: 1px 1px 0px rgb(121, 121, 121);\r\n        &.on {\r\n          background-color: #99CC00;\r\n        }\r\n        .ctrl {\r\n          background-color: #E57373;\r\n        }\r\n      }\r\n      .nav-box {\r\n        background-color: rgba(121, 121, 121, 0.7);\r\n        .nav-left-box,\r\n        .nav-right-box {\r\n          display: inline-block;\r\n          text-align: center;\r\n          width: 50px;\r\n          height: 50px;\r\n          margin: 0;\r\n          padding: 0;\r\n          line-height: 50px;\r\n          .ctrl {\r\n            margin: 0;\r\n            padding: 0;\r\n            &:hover {\r\n              background-color: rgba(153, 204, 0, 0.7);\r\n            }\r\n          }\r\n        }\r\n      }\r\n      .ctrl {\r\n        height: 35px;\r\n        width: 35px;\r\n        display: inline-block;\r\n        padding: 0px;\r\n        font-size: 28px;\r\n        color: #fff;\r\n        text-align: center;\r\n        white-space: nowrap;\r\n        border: none;\r\n        border-radius: 50%;\r\n        cursor: pointer;\r\n        margin: auto;\r\n        position: relative;\r\n        vertical-align: middle;\r\n        box-shadow: none;\r\n        line-height: 46px;\r\n        margin: 10px 0px 0px 0px;\r\n        border: 3px solid transparent;\r\n        &.on {\r\n          border: 3px solid rgb(153, 204, 0);\r\n        }\r\n      }\r\n\r\n      .add-shapebox {\r\n        background-color: rgba(49, 49, 49, 0.7);\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "src/assets/less/plugin-selected-item-editor.less",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin.plugin-selected-item-editor {\n  position: relative;\n  .auto-fill-up-form {\n    display: none;\n    height: 60px;\n    width: 700px;\n    color: #000;\n    background-color: #797979;\n    position: absolute;\n    bottom: 5px;\n    z-index: 900;\n    .message,\n    .form-item {\n      float: left;\n    }\n    .message {\n      width: 520px;\n      color: white;\n      font-size: 16px;\n      padding: 21px 0px 0px 18px;\n    }\n    .form-item {\n\n    }\n    &.on {\n      display: block;\n    }\n    &:after {\n      clear: both;\n    }\n    .form-item {\n      .form-ctrl-item-yes {\n        height: 60px;\n        width: 90px;\n        background-color: #1485cc;\n        border: none;\n        color: white;\n        font-size: 16px;\n        font-weight: bold;\n      }\n      .form-ctrl-item-no {\n        height: 60px;\n        width: 90px;\n        background-color: #0b486c;\n        border: none;\n        color: white;\n        font-size: 16px;\n        font-weight: bold;\n      }\n    }\n\n  }\n  .form {\n    height: 50px;\n    width: 100%;\n    color: #000;\n\n    .form-item {\n      float: left;\n\n      .form-item-label {\n        height: 60px;\n        background-color: #f2f2f2;\n        width: 170px;\n        border: none;\n        color: #666666;\n        font-size: 16px;\n        text-align: center;\n        font-weight: normal;\n      }\n      .form-item-tcin,\n      .form-item-tcout {\n        height: 60px;\n        width: 100px;\n        border: none;\n        color: #666666;\n        font-size: 16px;\n        text-align: center;\n        &.error {\n          background-color: #ba6868;\n        }\n      }\n      .form-ctrl-item-tcin,\n      .form-ctrl-item-tcout {\n        background-color: #1485cc;\n        border-radius: 0;\n        color: #FFFFFF;\n        display: block;\n        font-size: 16px;\n        height: 60px;\n        line-height: 20px;\n        padding: 10px;\n        text-transform: uppercase;\n        outline: none;\n        width: 70px;\n        border: none;\n        padding: 0px;\n\n      }\n      .form-ctrl-item-valid {\n        background-color: #797979;\n        height: 60px;\n        border: none;\n        color: white;\n        font-size: 16px;\n        &.valid {\n          background-color: #1485cc;\n        }\n        &:hover {\n          background-color: #6ab6e8;\n        }\n      }\n      .form-ctrl-item-delete {\n        background-color: #B71C1C;\n        height: 60px;\n        border: none;\n        color: white;\n        font-size: 16px;\n        display: none;\n        &.on {\n          display: block;\n        }\n        &:hover {\n          background-color: #C62828;\n        }\n      }\n      .form-ctrl-item-close-item {\n        background-color: #B71C1C;\n        height: 60px;\n        border: none;\n        color: white;\n        font-size: 16px;\n        display: none;\n        &.on {\n          display: block;\n        }\n        &:hover {\n          background-color: #C62828;\n        }\n      }\n      .form-ctrl-item-auto-fill-up {\n        background-color: #a1a1a1;\n        width: 60px;\n        height: 60px;\n        border: none;\n        font-size: 52px;\n        font-weight: 100;\n        padding-top: 10px;\n        color: white;\n        &:hover {\n          background-color: #6ab6e8;\n        }\n      }\n      &.delete {\n        display: none;\n        &.on {\n          display: block;\n        }\n        .form-ctrl-item-delete {\n          background-color: #22baa0;\n        }\n      }\n      &.label {\n        background-color: #f2f2f2;\n        padding: 0px;\n        border: medium none;\n        border-radius: 0;\n        box-shadow: none;\n        display: inline-block;\n        margin: 0;\n        border: none;\n      }\n    }\n  }\n  &.ajs-plugin {\n    overflow: visible !important;\n  }\n}"
  },
  {
    "path": "src/assets/less/plugin-storyboard.less",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin.plugin-storyboard {\n  display: none;\n  color: #FFF;\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  z-index: 98;\n  top: 0;\n  .thumbnail {\n    height: 90px;\n    width: 160px;\n    background-color: #313131;\n    background-position: 0px -90px;\n    background-repeat: no-repeat;\n    position: absolute;\n    bottom: 45px;\n    margin-top: 0px;\n    margin-right: 0px;\n    margin-bottom: 3px;\n    left: 50%;\n    border-radius: 2px;\n    border: solid #000 2px;\n    z-index: 90;\n  }\n  .filmstrip {\n    position: absolute;\n    width: 100%;\n    text-align: center;\n    margin: 0;\n    padding: 0;\n    opacity: 0.7;\n    z-index: 90;\n    -webkit-filter: grayscale(0.3);\n    -moz-filter: grayscale(0.3);\n    -ms-filter: grayscale(0.3);\n    -o-filter: grayscale(0.3);\n    .storyboard-thumbnail {\n      background-color: #313131;\n      background-position: 0px -90px;\n      background-repeat: no-repeat;\n      position: absolute;\n      margin-top: 0px;\n      margin-right: 0px;\n      margin-bottom: 3px;\n      border-radius: 2px;\n      border: solid #000 2px;\n    }\n  }\n  .preview-storyboard {\n    text-align: center;\n    display: block;\n    position: absolute;\n    margin: 0px;\n    padding: 0px;\n    .thumbnail-overlay {\n      height: 100%;\n      width: 100%;\n      position: relative;\n      border: solid #000 2px;\n      visibility: visible;\n      z-index: 80;\n      filter: blur(5px);\n      -webkit-filter: blur(3px) grayscale(0.8);\n      -moz-filter: blur(3px) grayscale(0.8);\n      -ms-filter: blur(3px) grayscale(0.8);\n      -o-filter: blur(3px) grayscale(0.8);\n    }\n  }\n\n}"
  },
  {
    "path": "src/assets/less/plugin-text-sync.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Plugin text sync\r\n */\r\n.ajs-plugin.plugin-text-sync {\r\n  background: #e6e6e6;\r\n  margin: 0;\r\n  padding: 0;\r\n  color: black;\r\n  .header {\r\n    overflow: hidden;\r\n    zoom: 1;\r\n    display: block;\r\n    margin: 0;\r\n    padding: 0;\r\n    .resume {\r\n      background: #555;\r\n      color: white;\r\n      padding: 15px;\r\n      text-align: left;\r\n      font-size: 14px;\r\n      overflow: hidden;\r\n      zoom: 1;\r\n      .heading {\r\n        font-size: 24px;\r\n        font-weight: 100;\r\n        color: white;\r\n        line-height: 1.1;\r\n        padding: 0;\r\n        margin: 0;\r\n      }\r\n    }\r\n  }\r\n\r\n  .line-component {\r\n    padding: 10px;\r\n    overflow-y: auto;\r\n    height: 100%;\r\n    margin: 0;\r\n    .line {\r\n\r\n      box-sizing: border-box;\r\n      color: rgb(0, 0, 0);\r\n      display: list-item;\r\n      font-size: 14px;\r\n      list-style-image: none;\r\n      list-style-position: outside;\r\n      list-style-type: none;\r\n      margin-bottom: 0px;\r\n      margin-left: 0px;\r\n      margin-right: 0px;\r\n      margin-top: 0px;\r\n      overflow-x: hidden;\r\n      overflow-y: hidden;\r\n      text-align: left;\r\n      padding: 10px;\r\n      margin: 0;\r\n\r\n      .info {\r\n        box-sizing: border-box;\r\n        color: rgb(0, 0, 0);\r\n        display: block;\r\n        float: left;\r\n        font-family: Arial, Helvetica, sans-serif;\r\n        font-size: 14px;\r\n        margin-right: 10px;\r\n        text-align: left;\r\n        .thumb {\r\n          background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0IwMTkyREMwMTAwMTFFNDk0QTM5OTI4MzY1OEFGQUEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0IwMTkyREQwMTAwMTFFNDk0QTM5OTI4MzY1OEFGQUEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQjAxOTJEQTAxMDAxMUU0OTRBMzk5MjgzNjU4QUZBQSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQjAxOTJEQjAxMDAxMUU0OTRBMzk5MjgzNjU4QUZBQSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Ph56Pv8AAAJASURBVHja7JnLiupAEIY1BlG8492gK1HceseFD+0juNAHcCG6GlBBBVHEG15wfk5ARDj2JHYn00zVqglN1/91qquqE2en03HIbIpDciMAAiAAAiAAApDa1J9M0jStWCz6fD4rle33+9FoNJvNPgWA+nK5bP3WYr90v+8Z2CFUKBRsjBCmdzaA3++3EYDpXTW64vF4XK1W9/tdhFyn0xmNRr1eL+dD/Hywut3u9XoVmFVUtd1u/zxhGEuj8/lcqHoY1ocXUXXgdrtZEPeGvKgOWy2fz2cyGZyrwWBwOp1EFTJBlk6nS6USBqFQCKHf7/clayXC4fBjDAb5eqH1ev0YbzYbgb2QIEO2QejjDBwOh+FwKB8A7OufUTvNyVwuVzabVRRlMplcLhfJAKC+0Wigk8E4l8v1er3z+SzNjQzq6/W6rh4WCARarZbb7ZYDQFcfi8WeH74weDyeSqWCV2Q634sCQMTXarUX9S8MUI8B0mUikcAgEon8FgCor1ar8Xj8fxN0BtijPUbL0Gw2OTIoH6pPJpPvp4Hhpbnny2AeADHNVP/m1sKLwTxAKpX68ObFhcHOSsyFwTwAl7ulzvDcV1sHMJ1Oeb0H1IdgMGh1K4FOeLlc8vreiGS13W6t7oUWi4Xt3aixEDL0ycm0GfJiDEDTNNzEUcJEbaeiYH14ERVCevX9VRca+kNDAARAAAQgGGC329moj+mdDTAej20EYHpnV2L9N63EP7p1BuZCdIgJgAAIgAAIgAD+IsC3AAMAzUKl+/tbh2cAAAAASUVORK5CYII=');\r\n          cursor: pointer;\r\n          width: 64px;\r\n          background-size: cover;\r\n          height: 64px;\r\n        }\r\n\r\n        .badge {\r\n          border-radius: 3px;\r\n          border: none;\r\n          cursor: pointer;\r\n          display: inline-block;\r\n          min-width: 10px;\r\n          padding: 3px 7px;\r\n          font-size: 12px;\r\n          font-weight: 700;\r\n          color: #fff;\r\n          line-height: 1;\r\n          vertical-align: baseline;\r\n          white-space: nowrap;\r\n          text-align: center;\r\n          background-color: #999;\r\n          border-radius: 10px;\r\n          &.tcin {\r\n            margin: 4px 0;\r\n          }\r\n\r\n          &.tcout {\r\n            margin: 0 0 4px 0;\r\n          }\r\n          &.tcin,\r\n          &.tcout {\r\n            clear: both;\r\n            float: left;\r\n            border-radius: 3px;\r\n          }\r\n        }\r\n      }\r\n      &.on {\r\n        background-color: #d8d8d8;\r\n        .content {\r\n          .ajs-progress .ajs-progress-bar {\r\n            background-color: @mainColor;\r\n          }\r\n        }\r\n      }\r\n      &.on .info .badge.tcin,\r\n      &.on .info .badge.tcout {\r\n        background-color: @mainColor;\r\n        cursor: pointer;\r\n      }\r\n      .content {\r\n        overflow: hidden;\r\n        zoom: 1;\r\n        text-align: left;\r\n        font-size: 14px;\r\n\r\n        .heading {\r\n          text-align: left;\r\n          font-size: 14px;\r\n        }\r\n        .text {\r\n          text-align: left;\r\n          font-size: 14px;\r\n          .word.on {\r\n            font-weight: bold;\r\n          }\r\n        }\r\n\r\n        .ajs-progress {\r\n          border-radius: 3px;\r\n          width: 100%;\r\n          height: 2px;\r\n          .ajs-progress-bar {\r\n            background-color: #d8d8d8;\r\n            height: 1px;\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n}"
  },
  {
    "path": "src/assets/less/plugin-timeline.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n* Plugin time line\r\n*/\r\n@modulesSizeListNames: lg, md, sm, xs;\r\n@modulesSizeList: 110px, 80px, 60px, 25px;\r\n\r\n.ajs-plugin.plugin-timeline {\r\n  position: relative;\r\n  background-color: #000;\r\n  -webkit-touch-callout: none;\r\n  -webkit-user-select: none;\r\n  -khtml-user-select: none;\r\n  -moz-user-select: none;\r\n  -ms-user-select: none;\r\n  user-select: none;\r\n\r\n  .timeline-cursor {\r\n    border-left: 1px solid @mainColor;\r\n    display: block;\r\n    height: 100%;\r\n    width: 1px;\r\n    left: 0%;\r\n    position: absolute;\r\n    z-index: 100;\r\n    bottom: 30px;\r\n  }\r\n\r\n  .timeline-progress-container {\r\n    position: absolute;\r\n    width: 100%;\r\n    height: 102px;\r\n    background-color: @mainBackgroundColor;\r\n    z-index: 90;\r\n    right: 100%;\r\n  }\r\n\r\n  .timeaxis {\r\n    /* tools bar */\r\n    .toolsbar {\r\n      background-color: #3f3f3f;\r\n      padding: 5px 10px 0 10px;\r\n      height: 40px;\r\n      .ajs-row {\r\n        .ajs-col {\r\n          float: left;\r\n          position: relative;\r\n          min-height: 1px;\r\n          padding: 0px;\r\n          margin: 0px;\r\n          &:after {\r\n            clear: both;\r\n          }\r\n        }\r\n        .leftContainer {\r\n          width: 16.66666667%;\r\n          text-align: left;\r\n        }\r\n        .middleContainer {\r\n          width: 66.66666667%;\r\n          text-align: center;\r\n        }\r\n        .rightContainer {\r\n          width: 16.66666667%;\r\n          text-align: right;\r\n        }\r\n      }\r\n      button.ajs-icon {\r\n        display: inline-block;\r\n        color: white;\r\n        background-color: #333;\r\n        border-radius: 50%;\r\n        border: none;\r\n        box-shadow: none;\r\n        padding: px;\r\n        width: 30px;\r\n        height: 30px;\r\n        padding: 0 2px 0 1px;\r\n        line-height: 36px;\r\n        font-size: 20px;\r\n        &:hover {\r\n          background-color: @mainColor;\r\n        }\r\n      }\r\n    }\r\n    /**Module TimeAxe**/\r\n    .module-timeaxis {\r\n      position: relative;\r\n      background-color: #333;\r\n      height: 100px;\r\n      margin-top: 2px;\r\n      .line {\r\n        height: 20px;\r\n        position: absolute;\r\n        top: 30px;\r\n        width: 100%;\r\n        margin-top: 5px;\r\n        z-index: 99;\r\n        border-bottom: 1px solid white;\r\n      }\r\n\r\n      .line-content {\r\n        text-align: center;\r\n        position: absolute;\r\n        top: 50%;\r\n        width: 100%;\r\n        height: auto;\r\n        z-index: 99;\r\n      }\r\n\r\n      .time-grid {\r\n        height: 10px;\r\n        width: 1px;\r\n        padding-top: 2px;\r\n        display: block;\r\n        position: absolute;\r\n        border-right: 1px solid #FFF;\r\n      }\r\n\r\n      .time-grid span.time {\r\n        display: block;\r\n        width: 55px;\r\n        font-size: 12px;\r\n        position: relative;\r\n        top: -20px;\r\n        line-height: 15px;\r\n        left: 50%;\r\n        margin-left: -27px;\r\n        cursor: pointer;\r\n      }\r\n    }\r\n\r\n    .timeaxis-label {\r\n      display: none;\r\n    }\r\n    .toolbar-container {\r\n      position: absolute;\r\n      top: 0;\r\n      right: 20px;\r\n      .plugin-btn {\r\n        border: none;\r\n        box-shadow: none;\r\n        background: none;\r\n        color: #6c6c6c;\r\n        font-size: 17px;\r\n        line-height: 25px;\r\n      }\r\n    }\r\n    &.off {\r\n      height: 25px;\r\n      background-color: #333;\r\n      .toolsbar {\r\n        display: none;\r\n      }\r\n      .module-timeaxis {\r\n        display: none;\r\n      }\r\n      .timeaxis-label {\r\n        position: absolute;\r\n        top: 0;\r\n        display: block;\r\n        .label {\r\n          font-size: 11px;\r\n          padding: 0px 0px 0px 5px;\r\n          margin: 0;\r\n        }\r\n      }\r\n    }\r\n\r\n  }\r\n\r\n  /**Composants du timeline**/\r\n  .components {\r\n    display: block;\r\n    position: relative;\r\n    width: 100%;\r\n    overflow: hidden;\r\n\r\n    .cuepoints-component,\r\n    .segments-component,\r\n    .images-component,\r\n    .histogram-component,\r\n    .visual-component {\r\n      position: relative;\r\n      cursor: pointer;\r\n    }\r\n\r\n    .cuepoints-component .module-cuepoints,\r\n    .segments-component .module-segments,\r\n    .images-component .module-images,\r\n    .histogram-component .module-histogram,\r\n    .visual-component .module-visual {\r\n      position: relative;\r\n      background-color: #333;\r\n      height: 100px;\r\n      margin-top: 2px;\r\n      text-align: left;\r\n    }\r\n    .cuepoints-component.activated .module-cuepoints,\r\n    .segments-component.activated .module-segments,\r\n    .images-component.activated .module-images,\r\n    .histogram-component.activated .module-histogram,\r\n    .visual-component.activated .module-visual {\r\n      background-color: #3f3f3f;\r\n    }\r\n\r\n    .cuepoints-component .module-cuepoints .line-content,\r\n    .segments-component .module-segments .line-content,\r\n    .images-component .module-images .line-content,\r\n    .visual-component .module-visual .line-content {\r\n      position: absolute;\r\n      top: 50%;\r\n      width: 100%;\r\n      height: auto;\r\n      z-index: 99;\r\n    }\r\n\r\n    .generate-theme(4);\r\n\r\n    .generate-theme(@n, @i: 1) when (@i =< @n) {\r\n      @sizeName: extract(@modulesSizeListNames, @i);\r\n      .component.cuepoints-component.@{sizeName} .module-cuepoints,\r\n      .component.segments-component.@{sizeName} .module-segments,\r\n      .component.images-component.@{sizeName} .module-images,\r\n      .component.histogram-component.@{sizeName} .module-histogram,\r\n      .component.visual-component.@{sizeName} .module-visual {\r\n        height: extract(@modulesSizeList, @i);\r\n      }\r\n\r\n      .@{sizeName} .focus-container {\r\n        .ui-resizable-handle {\r\n\r\n          height: extract(@modulesSizeList, @i);\r\n\r\n        }\r\n      }\r\n      .generate-theme(@n, (@i + 1));\r\n    }\r\n\r\n    @sizeName: extract(@modulesSizeListNames, 4);\r\n\r\n    .cuepoints-component.@{sizeName} .module-cuepoints .line-content,\r\n    .segments-component.@{sizeName} .module-segments .line-content,\r\n    .images-component.@{sizeName} .module-images .line-content,\r\n    .histogram-component.@{sizeName} .module-histogram .line-content,\r\n    .visual-component.@{sizeName} .module-visual .line-content {\r\n      display: none;\r\n    }\r\n    .component.@{sizeName} .sub,\r\n    .component.@{sizeName} .bottom-toolbar-container,\r\n    .component.@{sizeName} .focus-container {\r\n      display: none;\r\n    }\r\n    .cuepoints-component .module-cuepoints .line,\r\n    .segments-component .module-segments .line,\r\n    .images-component .module-images .line,\r\n    .histogram-component .module-images .line,\r\n    .visual-component .module-visual .line {\r\n      background-color: #fff;\r\n      height: 1px;\r\n      position: absolute;\r\n      z-index: 98;\r\n      top: 2px;\r\n      width: 100%;\r\n    }\r\n\r\n    .cuepoints-component .module-cuepoints .cuepoint {\r\n      position: absolute;\r\n      vertical-align: middle;\r\n      cursor: pointer;\r\n      font-size: 20px;\r\n      line-height: 13px;\r\n      z-index: 98;\r\n      margin-left: -10px;\r\n      &.selected {\r\n        animation-name: ajs-bounce;\r\n        -webkit-animation-name: ajs-bounce;\r\n        animation-duration: 1.6s;\r\n        -webkit-animation-duration: 1.6s;\r\n        animation-timing-function: ease;\r\n        -webkit-animation-timing-function: ease;\r\n        transform-origin: 50% 100%;\r\n        -ms-transform-origin: 50% 100%;\r\n        -webkit-transform-origin: 50% 100%;\r\n        animation-iteration-count: infinite;\r\n        -webkit-animation-iteration-count: infinite;\r\n      }\r\n    }\r\n\r\n    .segments-component .module-segments .segment {\r\n      position: absolute;\r\n      height: 7px;\r\n      width: 1px;\r\n      float: left;\r\n      z-index: 99;\r\n      background-color: @mainColor;\r\n      cursor: pointer;\r\n      border: none;\r\n      margin: 0;\r\n      padding: 0;\r\n      .ui-resizable-e {\r\n        cursor: e-resize;\r\n        width: 7px;\r\n        right: -5px;\r\n        top: 0;\r\n        height: 100%;\r\n      }\r\n      .ui-resizable-w {\r\n        cursor: w-resize;\r\n        width: 7px;\r\n        left: -5px;\r\n        top: 0;\r\n        height: 100%;\r\n      }\r\n      .ui-resizable-handle {\r\n        position: absolute;\r\n        font-size: .1px;\r\n        z-index: 99999;\r\n        display: block;\r\n      }\r\n      &.selected {\r\n        /**hatch**/\r\n        animation-name: ajs-pulse;\r\n        -webkit-animation-name: ajs-pulse;\r\n        animation-duration: 2s;\r\n        -webkit-animation-duration: 2s;\r\n        animation-timing-function: ease-in-out;\r\n        -webkit-animation-timing-function: ease-in-out;\r\n        transform-origin: 50% 100%;\r\n        -ms-transform-origin: 50% 100%;\r\n        -webkit-transform-origin: 50% 100%;\r\n        animation-iteration-count: infinite;\r\n        -webkit-animation-iteration-count: infinite;\r\n        visibility: visible !important;\r\n      }\r\n      &.marker {\r\n        height: 2px;\r\n        margin: 0px;\r\n        padding: 0px;\r\n        top: 2px;\r\n        .ui-resizable-w {\r\n          cursor: w-resize;\r\n          width: 7px;\r\n          left: -5px;\r\n          top: -3px;\r\n          height: 8px;\r\n          background-color: inherit;\r\n        }\r\n        .ui-resizable-e {\r\n          cursor: e-resize;\r\n          width: 7px;\r\n          right: -5px;\r\n          top: -3px;\r\n          height: 8px;\r\n          background-color: inherit;\r\n        }\r\n      }\r\n\r\n    }\r\n    .visual-component .module-visual {\r\n      .cuepoint {\r\n        position: absolute;\r\n        vertical-align: middle;\r\n        cursor: pointer;\r\n        font-size: 20px;\r\n        line-height: 13px;\r\n        z-index: 98;\r\n        margin-left: -10px;\r\n        top: -2px;\r\n        &.selected {\r\n          animation-name: ajs-bounce;\r\n          -webkit-animation-name: ajs-bounce;\r\n          animation-duration: 1.6s;\r\n          -webkit-animation-duration: 1.6s;\r\n          animation-timing-function: ease;\r\n          -webkit-animation-timing-function: ease;\r\n          transform-origin: 50% 100%;\r\n          -ms-transform-origin: 50% 100%;\r\n          -webkit-transform-origin: 50% 100%;\r\n          animation-iteration-count: infinite;\r\n          -webkit-animation-iteration-count: infinite;\r\n        }\r\n      }\r\n      .segment {\r\n        position: absolute;\r\n        height: 7px;\r\n        width: 1px;\r\n        float: left;\r\n        z-index: 99;\r\n        background-color: @mainColor;\r\n        cursor: pointer;\r\n        .cuepoint {\r\n          top: -7px;\r\n          margin-right: -10px;\r\n        }\r\n        &.selected {\r\n          /**hatch**/\r\n          animation-name: ajs-hatch;\r\n          -webkit-animation-name: ajs-hatch;\r\n          animation-duration: 2s;\r\n          -webkit-animation-duration: 2s;\r\n          animation-timing-function: ease-in-out;\r\n          -webkit-animation-timing-function: ease-in-out;\r\n          transform-origin: 50% 100%;\r\n          -ms-transform-origin: 50% 100%;\r\n          -webkit-transform-origin: 50% 100%;\r\n          animation-iteration-count: infinite;\r\n          -webkit-animation-iteration-count: infinite;\r\n          visibility: visible !important;\r\n\r\n        }\r\n        &.marker {\r\n          height: 2px;\r\n          margin: 0px;\r\n          padding: 0px;\r\n          top: 2px;\r\n          .ui-resizable-w {\r\n            cursor: w-resize;\r\n            width: 15px;\r\n            left: 0px;\r\n            top: -6px;\r\n            height: 15px;\r\n            background-color: inherit;\r\n            position: absolute;\r\n          }\r\n          .ui-resizable-e {\r\n            cursor: e-resize;\r\n            width: 15px;\r\n            right: 0px;\r\n            top: -6px;\r\n            height: 15px;\r\n            background-color: inherit;\r\n            position: absolute;\r\n          }\r\n        }\r\n      }\r\n    }\r\n    .images-component .module-images .image {\r\n      position: absolute;\r\n      float: left;\r\n      z-index: 99;\r\n      background-color: none;\r\n      height: 50px;\r\n      top: -25px;\r\n      background-repeat: no-repeat;\r\n      line-height: 50px;\r\n      cursor: pointer;\r\n      &.on {\r\n        z-index: 100;\r\n        .content {\r\n        }\r\n        &.segment {\r\n          z-index: 100;\r\n          border: solid 1px @mainColor;\r\n          margin-top: -1px;\r\n          .content {\r\n            /*height: 45px;*/\r\n          }\r\n        }\r\n      }\r\n\r\n      .content {\r\n        height: 30px;\r\n        z-index: 1;\r\n        position: absolute;\r\n        top: 15px;\r\n      }\r\n\r\n      hr.flow {\r\n        border-color: #FFF;\r\n        color: #FFF;\r\n        display: block;\r\n        margin: 0;\r\n        padding: 0;\r\n        position: relative;\r\n        height: 0px;\r\n        top: 28px;\r\n        z-index: 0;\r\n        border-style: double;\r\n        border-width: 1px;\r\n        width: 100%;\r\n        &:after,\r\n        &:before {\r\n\r\n          display: inline-block;\r\n          font-family: FontAwesome;\r\n          font-size: 1.1em;\r\n          position: absolute;\r\n          top: -25px;\r\n        }\r\n        &:after {\r\n          right: -6px;\r\n          content: \"\\f111\";\r\n        }\r\n\r\n        &:before {\r\n          left: -3px;\r\n          content: \"\\f142\";\r\n        }\r\n      }\r\n    }\r\n    .histogram-component .module-histogram .cuepoint {\r\n      position: absolute;\r\n      vertical-align: middle;\r\n      cursor: pointer;\r\n      font-size: 16px;\r\n      line-height: 8px;\r\n      z-index: 98;\r\n      margin-left: -5px;\r\n    }\r\n    .histogram-component .module-histogram .label {\r\n      display: block;\r\n      position: absolute;\r\n      top: 0px;\r\n    }\r\n    .histogram-component .module-histogram .line-content {\r\n      position: relative;\r\n      height: 100%;\r\n    }\r\n\r\n    .cuepoints-component .module-cuepoints .label,\r\n    .segments-component .module-segments .label,\r\n    .images-component .module-images .label,\r\n    .histogram-component .module-histogram .label,\r\n    .visual-component .module-visual .label {\r\n      background-color: transparent;\r\n      -webkit-touch-callout: text;\r\n      -webkit-user-select: text;\r\n      -khtml-user-select: text;\r\n      -moz-user-select: text;\r\n      -ms-user-select: text;\r\n      user-select: text;\r\n      padding: 0px 0px 0px 5px;\r\n      margin: 0;\r\n      display: inline-block;\r\n    }\r\n    .cuepoints-component .module-cuepoints .callback,\r\n    .segments-component .module-segments .callback,\r\n    .images-component .module-images .callback,\r\n    .histogram-component .module-histogram .callback,\r\n    .visual-component .module-visual .callback {\r\n      background-color: transparent;\r\n      -webkit-touch-callout: text;\r\n      -webkit-user-select: text;\r\n      -khtml-user-select: text;\r\n      -moz-user-select: text;\r\n      -ms-user-select: text;\r\n      user-select: text;\r\n      padding: 0px 0px 0px 5px;\r\n      margin: 0;\r\n      display: inline-block;\r\n    }\r\n\r\n    .sub.cuepoints-component .module-cuepoints,\r\n    .sub.segments-component .module-segments,\r\n    .sub.images-component .module-images,\r\n    .sub.images-histogram .module-histogram,\r\n    .sub.visual-histogram .module-visual {\r\n      background-color: rgba(100, 100, 100, 0.68);\r\n    }\r\n    .focus-container {\r\n      left: 0%;\r\n      width: 222px;\r\n      display: block;\r\n      background-color: rgba(100, 100, 100, 0.68);\r\n      height: 50%;\r\n      border: 0px solid #333;\r\n      box-shadow: none;\r\n      position: absolute;\r\n      top: 0px;\r\n      display: block;\r\n      z-index: 100;\r\n      min-width: 41px;\r\n      &.zoom {\r\n        background-color: @mainBackgroundColor;\r\n        height: 100%;\r\n      }\r\n      .ui-resizable-handle {\r\n        background-color: #6c6c6c;\r\n        width: 20px;\r\n        height: 100%;\r\n        text-align: center;\r\n        vertical-align: middle;\r\n        display: inline-block;\r\n        margin-left: 0px;\r\n        margin-right: 0px;\r\n        &.ui-resizable-w {\r\n          float: left;\r\n          font-size: 20px;\r\n          position: relative;\r\n          &:before {\r\n            -webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\r\n            -ms-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\r\n            transform: translateX(-50%) translateY(-50%) rotate(-90deg);\r\n            display: block;\r\n            width: 20px;\r\n            height: 20px;\r\n            margin: 2px 0px 0px 12px;\r\n            padding: 0px;\r\n            position: absolute;\r\n            top: 50%;\r\n          }\r\n        }\r\n        &.ui-resizable-e {\r\n          float: right;\r\n          font-size: 20px;\r\n          &:before {\r\n            -webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\r\n            -ms-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\r\n            transform: translateX(-50%) translateY(-50%) rotate(-90deg);\r\n            display: block;\r\n            width: 20px;\r\n            height: 20px;\r\n            margin: 2px 0px 0px 12px;\r\n            padding: 0px;\r\n            position: absolute;\r\n            top: 50%;\r\n          }\r\n        }\r\n      }\r\n    }\r\n    .bottom-toolbar-container {\r\n      position: absolute;\r\n      bottom: 5px;\r\n      left: 5px;\r\n      display: block;\r\n      .focus-btn {\r\n        background-color: transparent;\r\n        border: none;\r\n        color: white;\r\n        font-size: 20px;\r\n        line-height: 20px;\r\n      }\r\n    }\r\n    .component.focus-off {\r\n      .focus-container {\r\n        display: none;\r\n      }\r\n      .bottom-toolbar-container {\r\n        display: block;\r\n      }\r\n      .sub {\r\n        display: none;\r\n      }\r\n    }\r\n    .component.focus-on {\r\n      .focus-container {\r\n        display: block;\r\n      }\r\n      .bottom-toolbar-container {\r\n        display: none;\r\n      }\r\n      .sub {\r\n        display: block;\r\n      }\r\n    }\r\n\r\n    @smallSizeName: extract(@modulesSizeListNames, 4);\r\n\r\n    .component.@{smallSizeName} .sub,\r\n    .component.@{smallSizeName} .bottom-toolbar-container,\r\n    .component.@{smallSizeName} .focus-container {\r\n      display: none;\r\n    }\r\n\r\n    .timecursor {\r\n      left: 0%;\r\n      position: absolute;\r\n      top: 0px;\r\n      width: 1px;\r\n      border-left: 1px solid @mainColor;\r\n      height: 100%;\r\n    }\r\n\r\n    .toolbar-container {\r\n      position: absolute;\r\n      top: 0;\r\n      right: 20px;\r\n\r\n      .plugin-btn {\r\n        border: none;\r\n        box-shadow: none;\r\n        background: none;\r\n        color: #6c6c6c;\r\n        font-size: 17px;\r\n        line-height: 25px;\r\n      }\r\n\r\n      .close-btn,\r\n      .sort-btn,\r\n      .expand-btn {\r\n        color: @BtToolContColor;\r\n        height: 28px;\r\n        line-height: 28px;\r\n        text-align: center;\r\n        width: 28px;\r\n        cursor: pointer;\r\n        border: none;\r\n        background: transparent;\r\n        font-size: 20px;\r\n      }\r\n      .nav-controls {\r\n        color: @BtToolContColor;\r\n        display: inline-block;\r\n        float: left;\r\n        line-height: 28px;\r\n        margin-right: 15px;\r\n        text-align: center;\r\n        vertical-align: middle;\r\n        font-size: 20px;\r\n        padding: 0;\r\n        .prev-control {\r\n          cursor: pointer;\r\n          border: none;\r\n          background: transparent;\r\n          color: @BtToolContColor;\r\n          font-size: 20px;\r\n          padding: 0;\r\n        }\r\n        .next-control {\r\n          cursor: pointer;\r\n          border: none;\r\n          background: transparent;\r\n          color: @BtToolContColor;\r\n          font-size: 20px;\r\n          padding: 0;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  /**Bar de controle**/\r\n  .module-nav-bar-container {\r\n    .info {\r\n      padding-left: 15px;\r\n      line-height: 30px;\r\n    }\r\n    .toolsbar {\r\n      height: 30px;\r\n      padding: 0px;\r\n      background-color: #3f3f3f;\r\n      button.ajs-icon {\r\n        border-radius: 0%;\r\n        width: 30px;\r\n        line-height: 25px;\r\n        border: none;\r\n        background-color: transparent;\r\n        color: white;\r\n      }\r\n    }\r\n    .leftContainer {\r\n      text-align: left;\r\n      border: none;\r\n      background-color: transparent;\r\n      float: left;\r\n      width: 16.66666667%;\r\n\r\n    }\r\n    .middleContainer {\r\n      text-align: center;\r\n      border: none;\r\n      background-color: transparent;\r\n      float: left;\r\n      width: 66.66666667%;\r\n\r\n    }\r\n\r\n    .rightContainer {\r\n      text-align: right;\r\n      border: none;\r\n      background-color: transparent;\r\n      float: right;\r\n      width: 16.66666667%;\r\n      .config-menu {\r\n        padding-right: 30px;\r\n        .config-btn {\r\n          width: 30px;\r\n          line-height: 29px;\r\n          cursor: pointer;\r\n          font-size: 2.4em;\r\n\r\n        }\r\n      }\r\n      .config-menu-list {\r\n        background: #FFF;\r\n        border: none;\r\n        box-shadow: none;\r\n        margin: 0px;\r\n        position: absolute;\r\n        bottom: 30px;\r\n        border-radius: 0px;\r\n        right: 1px;\r\n        width: 200px;\r\n        list-style: none;\r\n        padding: 5px 0px 5px 5px;\r\n        li {\r\n          padding-left: 5px;\r\n          color: #000;\r\n          cursor: pointer;\r\n          text-align: left;\r\n        }\r\n      }\r\n    }\r\n    &:after {\r\n      clear: both;\r\n    }\r\n\r\n  }\r\n  /**resizeable component**/\r\n  .ui-resizable-s {\r\n    display: block;\r\n    height: 30px;\r\n    width: 30px;\r\n    font-size: 30px;\r\n    position: absolute;\r\n    left: 100%;\r\n    bottom: 0;\r\n    margin-left: -45px;\r\n    padding-left: 15px;\r\n    display: inline-block;\r\n    vertical-align: middle;\r\n    line-height: 40px;\r\n    font-weight: normal;\r\n    font-style: normal;\r\n    cursor: s-resize;\r\n  }\r\n}"
  },
  {
    "path": "src/assets/less/plugin-watermark.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n.ajs-plugin.plugin-watermark {\r\n  display: block;\r\n  color: #FFF;\r\n  position: absolute;\r\n  width: 100%;\r\n  height: 100%;\r\n  z-index: 98;\r\n  top: 0;\r\n  .watermark {\r\n    background-color: transparent;\r\n    background-repeat: no-repeat;\r\n    position: absolute;\r\n  }\r\n}"
  },
  {
    "path": "src/assets/less/plugin.less",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Plugin\r\n */\r\n\r\n.ajs-plugin {\r\n  font-family: @fontMain;\r\n  color: #FFF;\r\n  clear: both;\r\n  overflow: hidden;\r\n  font-size: 11px;\r\n\r\n  .error {\r\n    text-align: center;\r\n  }\r\n\r\n  .loader {\r\n    text-align: center;\r\n    width: 100%;\r\n    top: 50%;\r\n    font-size: 3em;\r\n  }\r\n\r\n  /* Custom de la scrollbar */\r\n  &::-webkit-scrollbar,\r\n  & ::-webkit-scrollbar {\r\n    width: 10px;\r\n  }\r\n  &::-webkit-scrollbar-thumb,\r\n  & ::-webkit-scrollbar-thumb {\r\n    background: rgba(108, 108, 108, .6);\r\n  }\r\n}\r\n\r\n@import \"player-custom-control-bar\";\r\n@import \"plugin-timeline\";\r\n@import \"plugin-text-sync\";\r\n@import \"plugin-captions\";\r\n@import \"plugin-overlay\";\r\n@import \"plugin-watermark\";\r\n@import \"plugin-menu-contextuel\";\r\n@import \"plugin-editor\";\r\n@import \"plugin-selected-item-editor\";\r\n@import \"plugin-storyboard\";"
  },
  {
    "path": "src/assets/sass/_ajs-animations.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n@keyframes ajs-hatch {\n  0% {\n    transform: rotate(0deg) scaleY(0.6);\n  }\n  20% {\n    transform: rotate(-2deg) scaleY(1.05);\n  }\n  35% {\n    transform: rotate(2deg) scaleY(1);\n  }\n  50% {\n    transform: rotate(-2deg);\n  }\n  65% {\n    transform: rotate(1deg);\n  }\n  80% {\n    transform: rotate(-1deg);\n  }\n  100% {\n    transform: rotate(0deg);\n  }\n}\n\n@webkit-keyframes ajs-hatch {\n  0% {\n    -webkit-transform: rotate(0deg) scaleY(0.6);\n  }\n  20% {\n    -webkit-transform: rotate(-2deg) scaleY(1.05);\n  }\n  35% {\n    -webkit-transform: rotate(2deg) scaleY(1);\n  }\n  50% {\n    -webkit-transform: rotate(-2deg);\n  }\n  65% {\n    -webkit-transform: rotate(1deg);\n  }\n  80% {\n    -webkit-transform: rotate(-1deg);\n  }\n  100% {\n    -webkit-transform: rotate(0deg);\n  }\n}\n\n@keyframes ajs-bounce {\n  0% {\n    transform: translateY(0%) scaleY(0.6);\n  }\n  60% {\n    transform: translateY(-100%) scaleY(1.1);\n  }\n  70% {\n    transform: translateY(0%) scaleY(0.95) scaleX(1.05);\n  }\n  80% {\n    transform: translateY(0%) scaleY(1.05) scaleX(1);\n  }\n  90% {\n    transform: translateY(0%) scaleY(0.95) scaleX(1);\n  }\n  100% {\n    transform: translateY(0%) scaleY(1) scaleX(1);\n  }\n}\n\n@webkit-keyframes ajs-bounce {\n  0% {\n    -webkit-transform: translateY(0%) scaleY(0.6);\n  }\n  60% {\n    -webkit-transform: translateY(-100%) scaleY(1.1);\n  }\n  70% {\n    -webkit-transform: translateY(0%) scaleY(0.95) scaleX(1.05);\n  }\n  80% {\n    -webkit-transform: translateY(0%) scaleY(1.05) scaleX(1);\n  }\n  90% {\n    -webkit-transform: translateY(0%) scaleY(0.95) scaleX(1);\n  }\n  100% {\n    -webkit-transform: translateY(0%) scaleY(1) scaleX(1);\n  }\n}\n\n@keyframes ajs-pulse {\n  0% {\n    transform: scale(0.9);\n    opacity: 0.7;\n  }\n  50% {\n    transform: scale(1);\n    opacity: 1;\n  }\n  100% {\n    transform: scale(0.9);\n    opacity: 0.7;\n  }\n}\n\n@webkit-keyframes ajs-pulse {\n  0% {\n    -webkit-transform: scale(0.95);\n    opacity: 0.7;\n  }\n  50% {\n    -webkit-transform: scale(1);\n    opacity: 1;\n  }\n  100% {\n    -webkit-transform: scale(0.95);\n    opacity: 0.7;\n  }\n}\n\n// spinner\n@keyframes ajs-spinner {\n  to {transform: rotate(360deg);}\n}\n\n@webkit-keyframes ajs-spinner {\n  to {-webkit-transform: rotate(360deg);}\n}"
  },
  {
    "path": "src/assets/sass/_ajs-mixins.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n* Plugin custom control bar\n*/\n\n// Mixins : transiition\n/**\n * global animation\n * $properties transition\n * $param : Time duration (s), function\n */\n@mixin transition($property:all, $time-duration:1s, $function-style:ease-out) {\n\n  -webkit-transition: $property $time-duration $function-style;\n  -moz-transition: $property $time-duration $function-style;\n  -ms-transition: $property $time-duration $function-style;\n  -o-transition: $property $time-duration $function-style;\n  transition: $property $time-duration $function-style;\n}\n\n\n// Transformation\n@mixin transform-scale($scale:1.5) {\n  -webkit-transform: scale($scale);\n  -ms-transform: scale($scale);\n  transform: scale($scale);\n  transform: scale($scale);\n}\n\n"
  },
  {
    "path": "src/assets/sass/_ajs-webfont.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n\n\n\n@font-face {\n\tfont-family:\"ajs-webfont\";\n\tsrc:url(\"../fonts/ajs-webfont.eot?da2b33fb7481b6e5ffca9457ecddfd8e\");\n\tfont-weight:normal;\n\tfont-style:normal;\n}\n@font-face {\n\tfont-family:\"ajs-webfont\";\n\tsrc:url(\"../fonts/ajs-webfont.eot?da2b33fb7481b6e5ffca9457ecddfd8e\");\n\tsrc:url(\"../fonts/ajs-webfont.eot?#iefix\") format(\"embedded-opentype\"),\n\t\turl(\"data:application/x-font-woff;charset=utf-8;base64,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\") format(\"woff\"),\n\t\turl(\"../fonts/ajs-webfont.ttf?da2b33fb7481b6e5ffca9457ecddfd8e\") format(\"truetype\");\n\tfont-weight:normal;\n\tfont-style:normal;\n}\n\n.ajs-icon {\n\tfont-family:\"ajs-webfont\";\n\tdisplay:inline-block;\n\tvertical-align:middle;\n\tline-height:1;\n\tfont-weight:normal;\n\tfont-style:normal;\n\tspeak:none;\n\ttext-decoration:inherit;\n\ttext-transform:none;\n\ttext-rendering:auto;\n\t-webkit-font-smoothing:antialiased;\n\t-moz-osx-font-smoothing:grayscale;\n}\n\n\n// Icons\n\n\n.ajs-icon-account:before {\n\tcontent:\"\\f101\";\n}\n\n\n.ajs-icon-add-whole-segment:before {\n\tcontent:\"\\f102\";\n}\n\n\n.ajs-icon-amalia-js:before {\n\tcontent:\"\\f103\";\n}\n\n\n.ajs-icon-arrows-h:before {\n\tcontent:\"\\f104\";\n}\n\n\n.ajs-icon-arrows-v:before {\n\tcontent:\"\\f105\";\n}\n\n\n.ajs-icon-bell:before {\n\tcontent:\"\\f106\";\n}\n\n\n.ajs-icon-building:before {\n\tcontent:\"\\f107\";\n}\n\n\n.ajs-icon-caret-right:before {\n\tcontent:\"\\f108\";\n}\n\n\n.ajs-icon-cercle:before {\n\tcontent:\"\\f109\";\n}\n\n\n.ajs-icon-check:before {\n\tcontent:\"\\f10a\";\n}\n\n\n.ajs-icon-chevron-circle-down:before {\n\tcontent:\"\\f10b\";\n}\n\n\n.ajs-icon-chevron-circle-left:before {\n\tcontent:\"\\f10c\";\n}\n\n\n.ajs-icon-chevron-circle-right:before {\n\tcontent:\"\\f10d\";\n}\n\n\n.ajs-icon-chevron-circle-up:before {\n\tcontent:\"\\f10e\";\n}\n\n\n.ajs-icon-chevron-down:before {\n\tcontent:\"\\f10f\";\n}\n\n\n.ajs-icon-chevron-left:before {\n\tcontent:\"\\f110\";\n}\n\n\n.ajs-icon-chevron-right:before {\n\tcontent:\"\\f111\";\n}\n\n\n.ajs-icon-chevron-up:before {\n\tcontent:\"\\f112\";\n}\n\n\n.ajs-icon-chromecast-active:before {\n\tcontent:\"\\f113\";\n}\n\n\n.ajs-icon-chromecast:before {\n\tcontent:\"\\f114\";\n}\n\n\n.ajs-icon-circle:before {\n\tcontent:\"\\f115\";\n}\n\n\n.ajs-icon-cog:before {\n\tcontent:\"\\f116\";\n}\n\n\n.ajs-icon-cogs:before {\n\tcontent:\"\\f117\";\n}\n\n\n.ajs-icon-comment:before {\n\tcontent:\"\\f118\";\n}\n\n\n.ajs-icon-compress:before {\n\tcontent:\"\\f119\";\n}\n\n\n.ajs-icon-control-backward:before {\n\tcontent:\"\\f11a\";\n}\n\n\n.ajs-icon-control-fast-forward:before {\n\tcontent:\"\\f11b\";\n}\n\n\n.ajs-icon-control-fast-rewind:before {\n\tcontent:\"\\f11c\";\n}\n\n\n.ajs-icon-control-forward:before {\n\tcontent:\"\\f11d\";\n}\n\n\n.ajs-icon-control-pause:before {\n\tcontent:\"\\f11e\";\n}\n\n\n.ajs-icon-control-play:before {\n\tcontent:\"\\f11f\";\n}\n\n\n.ajs-icon-control-rewind:before {\n\tcontent:\"\\f120\";\n}\n\n\n.ajs-icon-controlbar-compress:before {\n\tcontent:\"\\f121\";\n}\n\n\n.ajs-icon-controlbar-fullscreen:before {\n\tcontent:\"\\f122\";\n}\n\n\n.ajs-icon-controlbar-pause:before {\n\tcontent:\"\\f123\";\n}\n\n\n.ajs-icon-controlbar-play:before {\n\tcontent:\"\\f124\";\n}\n\n\n.ajs-icon-controlbar-settings:before {\n\tcontent:\"\\f125\";\n}\n\n\n.ajs-icon-controlbar-volume-left-off:before {\n\tcontent:\"\\f126\";\n}\n\n\n.ajs-icon-controlbar-volume-left:before {\n\tcontent:\"\\f127\";\n}\n\n\n.ajs-icon-controlbar-volume-min:before {\n\tcontent:\"\\f128\";\n}\n\n\n.ajs-icon-controlbar-volume-off:before {\n\tcontent:\"\\f129\";\n}\n\n\n.ajs-icon-controlbar-volume-right-off:before {\n\tcontent:\"\\f12a\";\n}\n\n\n.ajs-icon-controlbar-volume-right:before {\n\tcontent:\"\\f12b\";\n}\n\n\n.ajs-icon-controlbar-volume_max:before {\n\tcontent:\"\\f12c\";\n}\n\n\n.ajs-icon-controlbar-volume_middle:before {\n\tcontent:\"\\f12d\";\n}\n\n\n.ajs-icon-download:before {\n\tcontent:\"\\f12e\";\n}\n\n\n.ajs-icon-eject:before {\n\tcontent:\"\\f12f\";\n}\n\n\n.ajs-icon-ellipsis-h:before {\n\tcontent:\"\\f130\";\n}\n\n\n.ajs-icon-ellipsis-v:before {\n\tcontent:\"\\f131\";\n}\n\n\n.ajs-icon-eraser:before {\n\tcontent:\"\\f132\";\n}\n\n\n.ajs-icon-expand:before {\n\tcontent:\"\\f133\";\n}\n\n\n.ajs-icon-eye-off:before {\n\tcontent:\"\\f134\";\n}\n\n\n.ajs-icon-eye-on:before {\n\tcontent:\"\\f135\";\n}\n\n\n.ajs-icon-facetime:before {\n\tcontent:\"\\f136\";\n}\n\n\n.ajs-icon-female:before {\n\tcontent:\"\\f137\";\n}\n\n\n.ajs-icon-github:before {\n\tcontent:\"\\f138\";\n}\n\n\n.ajs-icon-information:before {\n\tcontent:\"\\f139\";\n}\n\n\n.ajs-icon-jogs-backward-0x:before {\n\tcontent:\"\\f13a\";\n}\n\n\n.ajs-icon-jogs-backward-1x:before {\n\tcontent:\"\\f13b\";\n}\n\n\n.ajs-icon-jogs-backward-2x:before {\n\tcontent:\"\\f13c\";\n}\n\n\n.ajs-icon-jogs-backward-3x:before {\n\tcontent:\"\\f13d\";\n}\n\n\n.ajs-icon-jogs-backward-4x:before {\n\tcontent:\"\\f13e\";\n}\n\n\n.ajs-icon-jogs-center:before {\n\tcontent:\"\\f13f\";\n}\n\n\n.ajs-icon-jogs-fast-backward:before {\n\tcontent:\"\\f140\";\n}\n\n\n.ajs-icon-jogs-fast-forward:before {\n\tcontent:\"\\f141\";\n}\n\n\n.ajs-icon-jogs-forward-0x:before {\n\tcontent:\"\\f142\";\n}\n\n\n.ajs-icon-jogs-forward-1x:before {\n\tcontent:\"\\f143\";\n}\n\n\n.ajs-icon-jogs-forward-2x:before {\n\tcontent:\"\\f144\";\n}\n\n\n.ajs-icon-jogs-forward-3x:before {\n\tcontent:\"\\f145\";\n}\n\n\n.ajs-icon-jogs-forward-4x:before {\n\tcontent:\"\\f146\";\n}\n\n\n.ajs-icon-key:before {\n\tcontent:\"\\f147\";\n}\n\n\n.ajs-icon-legal:before {\n\tcontent:\"\\f148\";\n}\n\n\n.ajs-icon-list-close:before {\n\tcontent:\"\\f149\";\n}\n\n\n.ajs-icon-list-open:before {\n\tcontent:\"\\f14a\";\n}\n\n\n.ajs-icon-lock-close:before {\n\tcontent:\"\\f14b\";\n}\n\n\n.ajs-icon-lock-open:before {\n\tcontent:\"\\f14c\";\n}\n\n\n.ajs-icon-male:before {\n\tcontent:\"\\f14d\";\n}\n\n\n.ajs-icon-microphone-off:before {\n\tcontent:\"\\f14e\";\n}\n\n\n.ajs-icon-microphone-on:before {\n\tcontent:\"\\f14f\";\n}\n\n\n.ajs-icon-minus:before {\n\tcontent:\"\\f150\";\n}\n\n\n.ajs-icon-music:before {\n\tcontent:\"\\f151\";\n}\n\n\n.ajs-icon-picture:before {\n\tcontent:\"\\f152\";\n}\n\n\n.ajs-icon-plus:before {\n\tcontent:\"\\f153\";\n}\n\n\n.ajs-icon-power:before {\n\tcontent:\"\\f154\";\n}\n\n\n.ajs-icon-refresh:before {\n\tcontent:\"\\f155\";\n}\n\n\n.ajs-icon-remove:before {\n\tcontent:\"\\f156\";\n}\n\n\n.ajs-icon-reorder:before {\n\tcontent:\"\\f157\";\n}\n\n\n.ajs-icon-screenshot:before {\n\tcontent:\"\\f158\";\n}\n\n\n.ajs-icon-scrubber-cursor:before {\n\tcontent:\"\\f159\";\n}\n\n\n.ajs-icon-search:before {\n\tcontent:\"\\f15a\";\n}\n\n\n.ajs-icon-sign-in:before {\n\tcontent:\"\\f15b\";\n}\n\n\n.ajs-icon-sign-out:before {\n\tcontent:\"\\f15c\";\n}\n\n\n.ajs-icon-sort:before {\n\tcontent:\"\\f15d\";\n}\n\n\n.ajs-icon-sound-link-off:before {\n\tcontent:\"\\f15e\";\n}\n\n\n.ajs-icon-sound-link-on:before {\n\tcontent:\"\\f15f\";\n}\n\n\n.ajs-icon-stop:before {\n\tcontent:\"\\f160\";\n}\n\n\n.ajs-icon-transcription:before {\n\tcontent:\"\\f161\";\n}\n\n\n.ajs-icon-volume-down:before {\n\tcontent:\"\\f162\";\n}\n\n\n.ajs-icon-volume-off:before {\n\tcontent:\"\\f163\";\n}\n\n\n.ajs-icon-volume-up:before {\n\tcontent:\"\\f164\";\n}\n\n\n.ajs-icon-zoom-in:before {\n\tcontent:\"\\f165\";\n}\n\n\n.ajs-icon-zoom-out:before {\n\tcontent:\"\\f166\";\n}\n"
  },
  {
    "path": "src/assets/sass/_default-style.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-tooltip {\n  font-family: Arial, Helvetica, sans-serif;\n  color: #fff;\n  background: #0cf;\n  padding: 10px 5px 10px 5px;\n  max-width: 300px;\n  box-shadow: none;\n  border: none;\n  margin-top: -50px;\n  &:after {\n    content: \"\";\n    position: absolute;\n    left: 50%;\n    top: -20px;\n    width: 15px;\n    height: 15px;\n    box-shadow: none;\n    -webkit-transform: rotate(45deg);\n    -moz-transform: rotate(45deg);\n    -ms-transform: rotate(45deg);\n    -o-transform: rotate(45deg);\n    tranform: rotate(45deg);\n    border: none;\n    background: #0cf;\n    margin-left: -7.5px;\n  }\n}\n\n.ui-tooltip {\n  padding: 8px;\n  position: absolute;\n  z-index: 9999;\n  max-width: 300px;\n  -webkit-box-shadow: 0 0 5px #aaa;\n  box-shadow: 0 0 5px #aaa;\n}\n\n.ui-helper-hidden-accessible {\n  display: none;\n}\n\n.ui-selectable-helper {\n  position: absolute;\n  z-index: 100;\n  border: 1px dotted #eaeaea;\n}"
  },
  {
    "path": "src/assets/sass/_player-custom-control-bar.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n* Plugin custom control bar\n*/\n\n// Add option control layer on display\n.ajs-control-layer {\n  .ajs-icon {\n    position: absolute;\n    width: 100%;\n    top: 50%;\n    text-align: center;\n    z-index: 9999;\n    margin-top: -80px;\n\n    &.ajs-icon-controlbar-play,\n    &.ajs-icon-controlbar-pause {\n      color: white;\n      font-size: 90px;\n      opacity: .5;\n      @include transform-scale(1);\n      @include transition(all, 0.5s, ease-out);\n    }\n  }\n  // Transformation with scale up\n  &.scale-up {\n    .ajs-icon-controlbar-play,\n    .ajs-icon-controlbar-pause {\n      @include transform-scale(2);\n      @include transition(all, 0.5s, ease-out);\n    }\n  }\n}\n\n.ajs {\n  .plugin-custom-controlbar {\n    width: 100%;\n    background-color: #1d1d1d;\n    background-image: -webkit-gradient(linear, 100% 0, 0 100%, from(#232323), to(#1d1d1d));\n    background-image: -webkit-linear-gradient(top, #232323 0%, #1d1d1d 100%);\n    background-image: -moz-linear-gradient(top, #232323 0%, #1d1d1d 100%);\n    background-image: -o-linear-gradient(top, #232323 0%, #1d1d1d 100%);\n    background-image: linear-gradient(#232323, #1d1d1d);\n    overflow: visible;\n    margin: 0;\n    padding: 0;\n    z-index: 900;\n    opacity: .9;\n    position: relative;\n\n    .ajs-time-indicator {\n      bottom: 25px;\n      position: absolute;\n      width: 50px;\n      height: 20px;\n      border: none;\n      background-color: rgba(108, 108, 108, .5);\n      left: 50%;\n      margin-left: -25px;\n      font-size: 9px;\n      line-height: 20px;\n      text-align: center;\n      display: none;\n      border-radius: 5px;\n      padding: 0px 5px 0px 5px;\n      z-index: 1000;\n      .ajs-tooltip-text {\n        font-size: 9px;\n        line-height: 20px;\n        text-align: center;\n        color: white;\n      }\n    }\n    .ajs-row {\n      margin: 0;\n\n      .ajs-container {\n\n        display: block;\n        float: left;\n        width: 33.33333333%;\n        min-height: 70px;\n        height: 70px;\n        position: relative;\n        padding: 0;\n        margin: 0;\n        z-index: 0;\n        &.left-container {\n        }\n        &.middle-container {\n          text-align: center;\n        }\n        &.right-container {\n        }\n      }\n    }\n    .player {\n      top: 0;\n    }\n\n    .player-base-button {\n      background: none;\n      margin: 0 auto;\n      text-align: center;\n      width: 35px;\n      color: #fff;\n      display: inline-block;\n      cursor: pointer;\n\n      // Prev and Next base button\n      .ajs-icon-control-fast-rewind,\n      .ajs-icon-control-fast-forward {\n        font-size: 1.5em;\n        position: relative;\n        top: 15px;\n      }\n    }\n    .player-play-button,\n    .player-pause-button {\n      background: none;\n      cursor: pointer;\n      display: inline-block;\n      line-height: 0;\n      margin: 0 auto;\n      position: relative;\n      text-align: center;\n      width: 35px;\n      color: #fff;\n      font-size: 35px;\n      position: relative;\n      top: 18px;\n\n      &.on {\n        display: inline-block;\n      }\n      &.off {\n        display: none;\n      }\n      span.button-container {\n        position: relative;\n      }\n    }\n    .player-label {\n      display: inline-block;\n      width: 160px;\n      line-height: 30px;\n      margin-left: 3px;\n      margin-right: 3px;\n      height: 28px;\n      background: none;\n    }\n    .player-timelabel.time-display {\n      display: inline-block;\n      line-height: 18px;\n      padding-left: 15px;\n      height: 35px;\n      position: relative;\n      top: 26px;\n      min-width: 80px;\n      .time-current {\n        color: white;\n        display: inline-block;\n        font-size: 1.2em;\n      }\n      .time-separator {\n        color: white;\n        display: inline-block;\n        padding-left: 5px;\n        padding-right: 5px;\n        position: relative;\n        font-size: 1.2em;\n        top: 0;\n        &:before {\n          content: \"/\";\n        }\n      }\n      .time-duration {\n        color: white;\n        display: inline-block;\n        font-size: 1.2em;\n        position: relative;\n        top: 0;\n      }\n      // Disabled TC\n      &.off {\n        .time-separator {\n          display: none;\n        }\n      }\n    }\n    .player-fullscreen-button {\n      width: 35px;\n      color: #fff;\n      margin: 15px 0 0 20px;\n      cursor: pointer;\n      margin-right: 10px;\n      font-size: 35px;\n      position: absolute;\n      right: 0;\n    }\n\n    .player-volume-control {\n      vertical-align: middle;\n      font-size: 2em;\n      margin-right: 20px;\n      position: relative;\n      top: -6px;\n      height: 35px;\n      width: 50px;\n      padding-top: 0;\n      cursor: pointer;\n      float: right;\n      line-height: 35px;\n\n      canvas {\n        display: none;\n      }\n      .volume-control-btn {\n        top: 16px;\n        margin: 0;\n        padding: 0;\n        display: inline-block;\n        position: relative;\n        vertical-align: middle;\n        line-height: 35px;\n        width: 50px;\n        height: 35px;\n        right: 60px;\n\n        font-size: 1.4em;\n        text-align: center;\n        color: white;\n\n        span.ajs-icon {\n          position: absolute;\n          z-index: 90;\n          display: block;\n          line-height: 50px;\n          text-align: left;\n          width: inherit;\n          height: 35px;\n          vertical-align: middle;\n          text-align: left;\n        }\n        span.ajs-icon.ajs-icon-volume-on:before,\n        span.ajs-icon.ajs-icon-volume-off:before,\n        span.ajs-icon.ajs-icon-volume-up:before,\n        span.ajs-icon.ajs-icon-volume-down:before {\n          display: block;\n          width: 50px;\n          height: inherit;\n          text-align: left;\n        }\n        span.ajs-icon.ajs-icon-volume-off:before {\n          margin-left: -7px;\n        }\n        span.ajs-icon.ajs-icon-volume-up:before {\n          margin-left: 0;\n        }\n        span.ajs-icon.ajs-icon-volume-down:before {\n          margin-left: -4px;\n        }\n\n        /* Slider container : control volume using ui-handler */\n        .volume-slider-ctn {\n          display: block;\n          position: absolute;\n          z-index: 100;\n          background-color: $primaryBackgroundColor;\n          width: 70px;\n          height: 190px;\n          bottom: -70px;\n          margin-left: -15px;\n          &.on {\n            opacity: 1;\n            bottom: -20px;\n            @include transition(all, 0.5s, ease-out);\n          }\n          &.off {\n            opacity: 0;\n            bottom: -250px;\n            @include transition(all, 0.5s, ease-out);\n          }\n          .slider-volume {\n            // Background progress volume\n            &.ui-slider {\n              position: relative;\n\n              border: none;\n              border-radius: 0;\n              margin: 0 20px;\n            }\n            .ui-slider-range {\n              background: none;\n              background-color: $mainColor;\n              background-position: 0% 0%;\n              border-bottom-color: rgb(51, 51, 51);\n              border-radius: none;\n              bottom: 0;\n              color: rgb(51, 51, 51);\n              height: 140px;\n              left: 0;\n              line-height: 30px;\n              position: absolute;\n              text-align: left;\n              width: 5px;\n              z-index: 1;\n            }\n            &.ui-slider-vertical {\n              background-color: $primaryBackgroundColor;\n              height: 140px;\n              top: 25px;\n              width: 5px;\n              left: 10px;\n            }\n            &.ui-widget-content {\n              background: none;\n              background-color: black;\n              border: none;\n              margin-left: 20px;\n              outline: none;\n            }\n            .ui-slider-handle.ui-state-default.ui-corner-all {\n              position: absolute;\n              z-index: 2;\n              border: solid 1px lighten($secondaryBackgroundColor, 50%);\n              background: none;\n              background-color: $secondaryBackgroundColor;\n              background-repeat: no-repeat;\n              box-shadow: none;\n              border-radius: 50px;\n              cursor: pointer;\n              display: block;\n              height: 15px;\n              width: 16px;\n              left: -5px;\n              right: 25px;\n              margin-bottom: -8px;\n              outline: none;\n              filter: none;\n            }\n          }\n        }\n      }\n    }\n\n    //Media queries on element only\n    .xs .player-volume-control {\n      display: none;\n    }\n    .player-progress-bar {\n      height: 10px;\n      margin-bottom: 0;\n      overflow: hidden;\n      background-color: #6a6969;\n      border-radius: 0;\n      clear: both;\n      position: relative;\n      text-align: left;\n      color: #404040;\n      z-index: 50;\n\n      .buffer-bar {\n        display: block;\n        height: 100%;\n        background-color: #555;\n        position: relative;\n        top: 0;\n        box-sizing: border-box;\n        width: 0;\n      }\n      .ui-slider-range {\n        float: left;\n        width: 0;\n        height: 100%;\n        font-size: 12px;\n        line-height: 20px;\n        color: #fff;\n        text-align: center;\n        background-color: #58c8b0;\n        position: absolute;\n        z-index: 1;\n        display: block;\n        border: 0;\n        background-position: 0 0;\n      }\n\n      .ui-slider-handle {\n        background-color: #fff;\n        border-radius: 0%;\n        cursor: pointer;\n        float: right;\n        height: 10px;\n        right: 0;\n        width: 10px;\n        z-index: 2;\n        text-shadow: none;\n        margin-left: -.6em;\n        position: absolute;\n        background-repeat: no-repeat;\n        background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));\n        background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\n        background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);\n        background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\n        background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\n        background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);\n        filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);\n        color: #333;\n        font-size: 13px;\n        line-height: normal;\n        border: 1px solid #ccc;\n        border-bottom-color: #bbb;\n        -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n        -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);\n        -webkit-transition: 0.1s linear background-image;\n        -moz-transition: 0.1s linear background-image;\n        -ms-transition: 0.1s linear background-image;\n        -o-transition: 0.1s linear background-image;\n        transition: 0.1s linear background-image;\n        overflow: visible;\n        top: -2px;\n      }\n      &.ui-slider .ui-slider-range {\n        background: $mainGradient;\n        float: left;\n        width: 0;\n        height: 100%;\n        font-size: 12px;\n        line-height: 20px;\n        color: #fff;\n        text-align: center;\n        left: 0;\n        top: 0;\n        position: absolute;\n        z-index: 1;\n        display: block;\n        border: 0;\n        background-position: 0 0;\n        color: #ffffff;\n        background-color: #0064cd;\n        background-repeat: repeat-x;\n        text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n        -webkit-border-radius: 0;\n        -moz-border-radius: 0;\n        border-radius: 0;;\n        border-color: #0064cd #0064cd #003f81;\n        border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);\n      }\n      &.ui-widget-content {\n        background: #6c6c6c;\n        border: none;\n        outline: none;\n      }\n    }\n    .player-channel-volume-control-position {\n      display: none;\n      position: relative;\n      width: 125px;\n      margin: 0 auto;\n\n      @media (min-width: $screen-md-min) {\n        display: block;\n      }\n\n      .player-channel-volume-control {\n        padding-top: 0;\n        cursor: pointer;\n        height: 35px;\n        width: 35px;\n        line-height: 30px;\n        color: #fff;\n\n        .volume-control-btn {\n          vertical-align: middle;\n          padding-top: 15px;\n          top: 0;\n          margin-left: 54px;\n          margin-top: 8px;\n          height: 35px;\n          line-height: 30px;\n          display: block;\n          font-size: 40px;\n        }\n        // Channel 2 canal volume\n        .channel-volume-sliders {\n          position: absolute;\n          bottom: -252px;\n          padding-left: 0;\n          height: 225px;\n          background-repeat: no-repeat;\n          left: 24px;\n          display: block;\n          background: linear-gradient(90deg, $backgroundColor 50%, $secondaryBackgroundColor 50%);\n          opacity: 0;\n          @include transition(all, 0.5s, ease-in);\n          &.on {\n            bottom: -28px;\n            display: block;\n            opacity: 1;\n            @include transition(all, 0.5s, ease-out);\n          }\n          .channel-volume-info {\n            display: block;\n            position: relative;\n            padding-left: 5px;\n            top: 2px;\n            .channel-volume-info-left,\n            .channel-volume-info-right {\n              width: 38px;\n              height: 40px;\n              color: #fff;\n              float: left;\n              text-align: center;\n              margin-left: 0;\n              font-size: 25px;\n              padding-left: 2px;\n              position: relative;\n              top: 8px;\n            }\n\n            .channel-volume-info-left {\n              text-align: left;\n            }\n            .channel-volume-info-right {\n              text-align: center;\n            }\n            .channel-volume-info-mid {\n              width: 12px;\n              height: 40px;\n              color: #fff;\n              float: left;\n              text-align: center;\n              padding-top: 10px;\n              left: 30px;\n              margin-left: 0;\n              .unify {\n                color: #EEE;\n                font-size: 23px;\n                font-weight: bold;\n                position: relative;\n                left: -8px;\n                &.on {\n                  color: cadetblue;\n                }\n                // Adjust position unlink\n                &.ajs-icon-sound-link-on {\n                  top: 6px;\n                }\n                &.ajs-icon-sound-link-off {\n                  top: 3px;\n                }\n              }\n            }\n          }\n          .channel-volume-control {\n            display: block;\n            margin: 0;\n            top: 10px;\n            position: relative;\n\n            .channel-volume-control-left,\n            .channel-volume-control-right {\n              height: 145px;\n              color: #fff;\n              float: left;\n              width: 30px;\n              margin-left: 19px;\n\n              .ui-state-focus,\n              .ui-state-hover {\n\n              }\n              .ui-slider .ui-slider-handle {\n                position: absolute;\n                z-index: 2;\n                width: 16px;\n                height: 16px;\n                cursor: default;\n                -ms-touch-action: none;\n                touch-action: none;\n                border-radius: 100%;\n              }\n              // Cursor volume\n              .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {\n                border: 1px solid lighten($secondaryBackgroundColor, 45%);\n                background: none;\n                background: $mainBackgroundColor;\n                font-weight: normal;\n                color: #454545;\n                cursor: pointer;\n                outline: none;\n              }\n              .ui-slider-vertical {\n                width: 4px;\n                height: 140px;\n              }\n              // Progress volume\n              .ui-widget-content {\n                border: 2px solid lighten(black, 10%);\n                background: none;\n                background-color: lighten(black, 15%);\n                cursor: pointer;\n                outline: none;\n                position: relative;\n              }\n              .ui-slider-vertical .ui-slider-handle {\n                left: -8px;\n                margin-left: 0;\n                margin-bottom: -.6em;\n              }\n            }\n            .channel-volume-control-mid {\n              width: 35px;\n              height: 3px;\n              background-color: $secondaryColor;\n              clear: both;\n              cursor: default;\n              background-repeat: no-repeat;\n              margin-left: 5px;\n              position: absolute;\n              visibility: hidden;\n              left: 24px;\n              top: 39px;\n              &.on {\n                visibility: visible;\n                top: 39px;\n              }\n            }\n          }\n        }\n      }\n    }\n    .player-jog-shuttle-button {\n      line-height: 30px;\n      color: #fff;\n      display: inline-block;\n      width: 240px;\n      position: relative;\n      border: 1px solid #cacaca;\n      border-radius: 20px;\n      height: 38px;\n      top: 18px;\n      margin: 0 30px 0 30px;\n      @media(min-width: 50px) and (max-width: 1058px) {\n        display: none;\n      }\n\n      span.button-container {\n        border: none;\n        border-radius: 0%;\n        display: inline-block;\n        height: 32px;\n        line-height: 40px;\n        font-size: 35px;\n        width: 100%;\n        position: relative;\n        top: 2px;\n\n        .jog-shuttle-separator {\n          display: inline-block;\n          width: 2px;\n          background-color: #eee;\n          height: 32px;\n          position: absolute;\n          right: 50%;\n        }\n        .backward-container,\n        .forward-container {\n          height: 32px;\n          display: inline-block;\n          line-height: 30px;\n          float: none;\n          color: rgba(238, 238, 238, 0.25);\n          margin-top: 5px;\n          padding-right: 5px;\n          padding-left: 5px;\n          span {\n            height: 32px;\n            padding-left: 10px;\n            padding-right: 10px;\n            position: absolute;\n            color: #66cc99;\n            top: 100px;\n\n            &.ajs-icon-jogs-forward-0x,\n            &.ajs-icon-jogs-backward-0x {\n              visibility: hidden;\n            }\n            &.ajs-icon-jogs-backward-1x,\n            &.ajs-icon-jogs-backward-2x,\n            &.ajs-icon-jogs-backward-3x,\n            &.ajs-icon-jogs-backward-4x,\n            &.ajs-icon-jogs-fast-backward {\n              left: 0;\n            }\n\n            &.ajs-icon-jogs-forward-1x,\n            &.ajs-icon-jogs-forward-2x,\n            &.ajs-icon-jogs-forward-3x,\n            &.ajs-icon-jogs-forward-4x,\n            &.ajs-icon-jogs-fast-forward {\n              color: #66cc99;\n              right: -5px;\n              top: 2px;\n            }\n            &.ajs-icon-jogs-backward-1x,\n            &.ajs-icon-jogs-backward-2x,\n            &.ajs-icon-jogs-backward-3x,\n            &.ajs-icon-jogs-backward-4x,\n            &.ajs-icon-jogs-fast-backward {\n              color: #66cc99;\n              left: -5px;\n              top: 2px;\n            }\n          }\n        }\n        .backward-container {\n          float: left;\n          text-align: left;\n          border: none;\n          width: 74px;\n        }\n        .forward-container {\n          float: right;\n          text-align: right;\n          width: 74px;\n        }\n        .jog-shuttle {\n          background-color: transparent;\n          border-radius: 20px;\n          position: absolute;\n          width: 151px;\n          top: 0;\n          height: 32px;\n          margin-left: 43px;\n          -webkit-transition: opacity 0.5s ease-in;\n          -moz-transition: opacity 0.5s ease-in;\n          -o-transition: opacity 0.5s ease-in;\n          -ms-transition: opacity 0.5s ease-in;\n          transition: opacity 0.5s ease-in;\n          display: block;\n          border: none;\n          &.on {\n            display: block;\n          }\n          span.ui-slider-handle {\n            border: 1px solid;\n            top: -8px;\n            margin: 0;\n            position: absolute;\n            z-index: 2;\n            cursor: pointer;\n            padding: 0;\n            border-radius: 100%;\n            background-color: #1D1D1D;\n            height: 48px;\n            width: 50px;\n            line-height: 30px;\n            margin-left: -25px;\n            font-size: 48px;\n            color: #eee;\n            font-weight: normal;\n            outline: none;\n          }\n        }\n      }\n    }\n  }\n\n  //Media queries for extra extra small <=320px container element only\n  &.xxs {\n    // Time display\n    .player-timelabel.time-display {\n      display: inline-block;\n      line-height: 18px;\n      padding-left: 15px;\n      height: 35px;\n      position: relative;\n      top: 18px;\n      min-width: 80px;\n\n      .time-current {\n        display: block;\n        font-size: 1.2em;\n      }\n      .time-separator {\n        display: inline-block;\n        font-size: 0.8em;\n      }\n      .time-duration {\n        font-size: 0.8em;\n      }\n    }\n    // Volume control\n    .player-volume-control,\n    .player-channel-volume-control-position {\n      display: none;\n    }\n  }\n\n  //Media queries for extra small container element only\n  &.xs {\n    // Time display\n    .player-timelabel.time-display {\n      display: inline-block;\n      line-height: 18px;\n      padding-left: 15px;\n      height: 35px;\n      position: relative;\n      top: 18px;\n      min-width: 80px;\n\n      .time-current {\n        display: block;\n        font-size: 1.2em;\n      }\n      .time-separator {\n        display: inline-block;\n        font-size: 0.8em;\n      }\n      .time-duration {\n        font-size: 0.8em;\n      }\n    }\n    // Volume control\n    .player-volume-control,\n    .player-channel-volume-control-position {\n      display: none;\n    }\n    // Jog Shuttle\n    .player-jog-shuttle-button {\n      display: none;\n    }\n  }\n\n  //Media queries for small container element only\n  &.sm {\n    // Time display\n    .player-timelabel.time-display {\n      top: 18px;\n      .time-current {\n        display: block;\n        font-size: 1.2em;\n      }\n      .time-separator {\n        display: inline-block;\n        font-size: 0.8em;\n      }\n      .time-duration {\n        font-size: 0.8em;\n      }\n\n      .player-volume-control-position,\n      .player-channel-volume-control {\n        display: none;\n      }\n    }\n\n    // Jog Shuttle\n    .player-jog-shuttle-button {\n      display: none;\n    }\n\n    // Volume position\n    .player-channel-volume-control-position {\n      margin-right: 40px;\n    }\n\n  }\n\n}"
  },
  {
    "path": "src/assets/sass/_player.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Player\n */\n/**\n * Plugin control bar\n */\n.ajs {\n  overflow: hidden;\n  font-family: $fontMain;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  line-height: 0px;\n  .ajs-loader {\n    color: white;\n    position: absolute;\n    z-index: 100;\n    text-align: center;\n    width: 100%;\n    height: 100%;\n    top: 50%;\n    font-size: 100px;\n    margin-top: -50px;\n\n    .ajs-icon {\n      $time-duration:800ms;\n      animation: ajs-spinner $time-duration linear infinite;\n      display: inline-block;\n      -webkit-animation: ajs-spinner $time-duration linear infinite;\n      transform-origin: 50% 50%;\n      line-height: 104px;\n      height: 70px;\n    }\n  }\n  .ajs-error {\n    position: absolute;\n    z-index: 100;\n    width: 100%;\n    height: 100%;\n    background-color: black;\n    color: white;\n    vertical-align: middle;\n    text-align: center;\n    line-height: 20px;\n    top: 0;\n    p {\n      margin-left: auto;\n      margin-right: auto;\n      position: relative;\n      top: 50%;\n      margin-top: -25px;\n    }\n  }\n  /* Reset background with font Awesome */\n  .ajs .ui-state-default .ui-icon[class*=\" fa-\"] {\n    background: none;\n    text-indent: 0;\n  }\n}\n\n/**tooltip**/\n.ui-tooltip {\n  font-family: $fontMain;\n  padding: 5px 5px;\n  color: white;\n  border: none;\n  box-shadow: none;\n  border: none;\n  box-shadow: none;\n  background: $mainColor;\n  p {\n    font-size: 14px;\n    margin: 0;\n  }\n  .timeline-images-component {\n    padding: 3px;\n    &.tooltip-image {\n      width: 200px;\n    }\n  }\n  .image {\n    max-width: 100%;\n  }\n}\n\n.ajs-arrow {\n  width: 70px;\n  height: 16px;\n  overflow: hidden;\n  position: absolute;\n  left: 50%;\n  margin-left: -35px;\n  bottom: -16px;\n  border: none;\n  &:after {\n    content: \"\";\n    position: absolute;\n    left: 20px;\n    top: -20px;\n    width: 25px;\n    height: 25px;\n    box-shadow: none;\n    -webkit-transform: rotate(45deg);\n    -moz-transform: rotate(45deg);\n    -ms-transform: rotate(45deg);\n    -o-transform: rotate(45deg);\n    tranform: rotate(45deg);\n    border: none;\n    background: $mainColor;\n  }\n\n  &.top {\n    top: -16px;\n    bottom: auto;\n    &:after {\n      bottom: -20px;\n      top: auto;\n    }\n  }\n  &.left {\n    left: 20%;\n  }\n}\n\n.inalabplayer-ui-resizable-helper {\n  z-index: 9999;\n  border: 1px dashed #6c6c6c !important;\n}"
  },
  {
    "path": "src/assets/sass/_plugin-captions.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Plugin caption\n */\n.ajs-plugin.plugin-caption {\n  display: block;\n  color: #FFF;\n  position: absolute;\n  bottom: 100px;\n  line-height: 20px;\n  text-align: center;\n  width: 100%;\n  background-color: $inverseBackgroundColor;\n  min-height: 20px;\n  z-index: 98;\n  padding-left: 0;\n  padding-right: 0;\n  font-size: 1.4em;\n  &.fullScreenOn {\n    font-size: 3em;\n    line-height: 45px;\n  }\n  &.fullScreenOff {\n    font-size: 1.4em;\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-editor.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin.editor {\n  position: relative;\n  .loader {\n    position: absolute;\n    top: 110px;\n    z-index: 100;\n    left: 0;\n    right: 0;\n    margin: 0;\n    padding: 0;\n  }\n  /**\n   * Plugin list editor\n   */\n  &.plugin-list-editor {\n    color: #666;\n    .heading {\n      height: 30px;\n      border: none;\n      line-height: 25px;\n      background-color: #616161;\n      color: #CCC;\n      .title {\n        text-align: left;\n        padding-left: 10px;\n        margin: 0;\n        color: #FFF;\n        line-height: 30px;\n        font-size: 14px;\n      }\n    }\n    .body {\n      ul.listOfmetadata {\n        box-sizing: border-box;\n        color: #cacaca;\n        display: block;\n        font-size: 14px;\n        line-height: 20px;\n        list-style-type: disc;\n        margin-bottom: 0px;\n        margin-top: 0px;\n        padding-left: 0px;\n        height: 90px;\n        overflow-y: overlay;\n        li.item {\n          position: relative;\n          display: block;\n          padding: 5px 15px;\n          margin: 0;\n          background-color: #eceff1;\n          border: none;\n          color: #000;\n          font-size: 11px;\n          &.selected {\n            background-color: #546e7a;\n            color: rgba(255, 255, 255, 0.87);\n          }\n          span.delete {\n            float: right;\n            padding: 0px;\n            font-size: 10px;\n            color: #fff;\n            text-align: center;\n            white-space: nowrap;\n            background-color: #E57373;\n            border: none;\n            border-radius: 50%;\n            cursor: pointer;\n            display: inline-block;\n            height: 20px;\n            width: 20px;\n            margin: auto;\n            position: relative;\n            vertical-align: middle;\n            box-shadow: none;\n            line-height: 23px;\n            margin-left: 5px;\n            &:before {\n              margin-left: -1px;\n            }\n          }\n          span.duplicate {\n            float: right;\n            padding: 0px;\n            font-size: 10px;\n            color: #fff;\n            text-align: center;\n            white-space: nowrap;\n            background-color: #3cf;\n            border: none;\n            border-radius: 50%;\n            cursor: pointer;\n            display: inline-block;\n            height: 20px;\n            width: 20px;\n            margin: auto;\n            position: relative;\n            vertical-align: middle;\n            box-shadow: none;\n            line-height: 23px;\n            &:before {\n              margin-left: -1px;\n            }\n          }\n        }\n      }\n    }\n    .footer {\n      height: 25px;\n      border: none;\n      line-height: 25px;\n      background-color: #616161;\n      color: #CCC;\n      text-align: center;\n\n      .add-metadata {\n        min-width: 10px;\n        font-size: 25px;\n        font-weight: normal;\n        color: #fff;\n        text-align: center;\n        white-space: nowrap;\n        background-color: #00CC99;\n        border: none;\n        border-radius: 50%;\n        cursor: pointer;\n        display: inline-block;\n        height: 35px;\n        width: 35px;\n        margin: auto;\n        position: relative;\n        vertical-align: middle;\n        margin-top: -25px;\n        box-shadow: none;\n        line-height: 40px;\n        text-align: left;\n        &:before {\n          margin-left: -1px;\n        }\n      }\n    }\n  }\n\n  /**\n   * Plugin block editor\n   */\n  &.plugin-block-editor {\n    color: #666;\n    .heading {\n      height: 30px;\n      border: none;\n      line-height: 25px;\n      background-color: #616161;\n      color: #CCC;\n      .title {\n        text-align: left;\n        padding-left: 10px;\n        margin: 0;\n        color: #FFF;\n        line-height: 30px;\n        font-size: 14px;\n      }\n    }\n    .body {\n      padding: 5px 5px 15px 5px;\n      form.formMetadataBlock {\n        .form-item {\n          padding: 0px;\n          margin: 0px;\n          box-shadow: none;\n          font-size: 12px;\n          .input {\n            width: 100%;\n            border: none;\n            box-shadow: none;\n            background-color: #eaeaea;\n            color: #000;\n            height: 25px;\n            padding-left: 5px;\n            border-radius: 0px;\n            font-size: 12px;\n          }\n          .text-area {\n            width: 100%;\n            height: 50px;\n            border-radius: 3px;\n            border: 1px solid #ccc;\n            display: inline-block;\n            padding: 4px;\n            font-size: 12px;\n          }\n          .select {\n            width: 100%;\n            border: none;\n            box-shadow: none;\n            background-color: #eaeaea;\n            color: #000;\n            height: 25px;\n            padding-left: 5px;\n            border-radius: 0px;\n            font-size: 12px;\n          }\n          .shape.ajs-icon {\n            font-size: 20px;\n          }\n        }\n      }\n      .messages-container {\n        padding: 0px;\n        margin-bottom: 0px;\n        border: none;\n        border-radius: 4px;\n        background-color: #E1F5FE;\n        margin-top: 0px;\n        text-align: center;\n        &.type-info {\n          background-color: #E1F5FE;\n        }\n        &.type-error {\n          background-color: #EF9A9A;\n        }\n        p.info {\n          padding: 0px;\n          margin: 0px;\n        }\n        p.error {\n          padding: 0px;\n          margin: 0px;\n        }\n      }\n    }\n    .footer {\n      height: 25px;\n      border: none;\n      line-height: 25px;\n      background-color: #616161;\n      color: #CCC;\n      text-align: center;\n      .save-metadata {\n        min-width: 10px;\n        padding: 0;\n        font-size: 25px;\n        font-weight: normal;\n        color: #fff;\n        text-align: center;\n        white-space: nowrap;\n        background-color: #03A9F4;\n        border: none;\n        border-radius: 50%;\n        cursor: pointer;\n        display: inline-block;\n        height: 35px;\n        width: 35px;\n        margin: auto;\n        position: relative;\n        vertical-align: middle;\n        margin-top: -25px;\n        box-shadow: none;\n        line-height: 40px;\n        &:before {\n          margin-left: -1px;\n        }\n      }\n    }\n  }\n  /**\n   * Plugin items editor\n   */\n  &.plugin-items-editor {\n    color: #616161;\n    .heading {\n      height: 30px;\n      border: none;\n      line-height: 25px;\n      background-color: #616161;\n      color: #CCC;\n      .title {\n        text-align: left;\n        padding-left: 10px;\n        margin: 0;\n        color: #FFF;\n        line-height: 30px;\n        font-size: 14px;\n      }\n\n    }\n    .body {\n      .messages-container {\n        padding: 5px 0px 5px 0px;\n        margin-bottom: 11px;\n        border: none;\n        border-radius: 4px;\n        background-color: #E1F5FE;\n        margin-top: 10px;\n        text-align: center;\n        min-height: 25px;\n        &.type-info {\n          background-color: #E1F5FE;\n        }\n        &.type-error {\n          background-color: #EF9A9A;\n        }\n        p.info {\n          padding: 0px;\n          margin: 0px;\n        }\n        p.error {\n          padding: 0px;\n          margin: 0px;\n        }\n      }\n      ul.listOfSelectedItems {\n        box-sizing: border-box;\n        color: #cacaca;\n        display: block;\n        font-size: 14px;\n        line-height: 20px;\n        list-style-type: disc;\n        margin-bottom: 0px;\n        margin-top: 0px;\n        padding-left: 0px;\n        height: 285px;\n        overflow-y: overlay;\n        li.item {\n          position: relative;\n          display: block;\n          padding: 0px;\n          margin-bottom: 0;\n          background-color: transparent;\n          border: none;\n          color: #000;\n          box-shadow: none;\n          font-size: 12px;\n          &.error {\n            border: 1px solid red;\n            text-align: left;\n          }\n          .form-data {\n            padding: 25px 0px 10px 0px;\n            border: none;\n            background-color: #eceff1;\n            margin-right: 0;\n            min-height: 56px;\n            box-shadow: none;\n            margin: 1px 0px 0px 0px;\n            .form-input {\n              margin: 0px 5px;\n              box-shadow: none;\n              min-height: 25px;\n              .input {\n                width: 100%;\n                border: none;\n                box-shadow: none;\n                background-color: #eaeaea;\n                color: #000;\n                height: 25px;\n                padding-left: 5px;\n                border-radius: 5px;\n                font-size: 12px;\n              }\n\n            }\n            .form-controls {\n              position: absolute;\n              width: auto;\n              height: 25px;\n              top: 5px;\n              right: 10px;\n              padding: 0px;\n              span.button {\n                padding: 0px;\n                font-size: 12px;\n                font-weight: normal;\n                text-align: center;\n                white-space: nowrap;\n                border: none;\n                border-radius: 50%;\n                cursor: pointer;\n                display: inline-block;\n                height: 20px;\n                width: 20px;\n                margin: 0px 0px 0px 5px;\n                position: relative;\n                vertical-align: middle;\n                padding-top: 0px;\n                margin-top: 0;\n                top: 0px;\n                right: 0px;\n                color: white;\n                box-shadow: none;\n                line-height: 23px;\n                opacity: 1;\n                filter: none;\n                float: none;\n                &:before {\n                  margin-left: -1px;\n                }\n                &.valid {\n                  background-color: #CACACA;\n                  visibility: hidden;\n                  &.valid.on {\n                    visibility: visible;\n                    background-color: #03A9F4;\n                  }\n                }\n                &.remove {\n                  background-color: #FFC107;\n                }\n                &.close {\n                  background-color: #757575;\n                }\n\n              }\n            }\n          }\n\n        }\n      }\n\n    }\n    .footer {\n      height: 25px;\n      border: none;\n      line-height: 25px;\n      background-color: #616161;\n      color: #CCC;\n      text-align: center;\n      .button {\n        min-width: 10px;\n        font-size: 25px;\n        font-weight: normal;\n        color: #fff;\n        text-align: center;\n        white-space: nowrap;\n        border: none;\n        border-radius: 50%;\n        cursor: pointer;\n        display: inline-block;\n        height: 35px;\n        width: 35px;\n        margin: 5px;\n        position: relative;\n        vertical-align: middle;\n        margin-top: -25px;\n        box-shadow: none;\n        line-height: 40px;\n        &.add {\n          background-color: #00CC99;\n          &:before {\n            margin-left: -1px;\n          }\n        }\n        &.validateAll {\n          background-color: #03A9F4;\n          &:before {\n            margin-left: -1px;\n          }\n        }\n        &.clear {\n          background-color: #757575;\n          &:before {\n            margin-left: -1px;\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-menu-contextuel.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/** Plugin menu contextuel**/\n.ajs .plugin-context-menu.dropdown-menu {\n  width: 300px;\n  padding: 0px 0px 0px 10px;\n  margin: 0;\n  text-align: left;\n  list-style: none;\n  background-color: #fff;\n  -webkit-background-clip: padding-box;\n  background-clip: padding-box;\n  border: 1px solid #ccc;\n  border: 1px solid rgba(0, 0, 0, .15);\n  border-radius: 4px;\n  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n  box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n  z-index: 1000;\n  &:before {\n    bottom: -14px;\n    border-top: 7px solid #555;\n  }\n  &:before, &:after, li:first-child:after {\n    content: '';\n    display: block;\n    position: absolute;\n    left: 15px;\n    width: 0;\n    height: 0;\n    border: 7px outset transparent;\n  }\n  li {\n    color: #838ca2;\n    text-shadow: none;\n    margin: 0;\n    padding: 0px 0px 0px 25px;\n    border: 0;\n    font-size: 100%;\n    font: inherit;\n    vertical-align: middle;\n    height: 30px;\n    list-style: none;\n    line-height: 30px;\n    display: block;\n    position: relative;\n\n    .ajs-icon {\n      position: absolute;\n      left: 0px;\n      top: 3px;\n      font-size: 24px;\n      line-height: 28px;\n    }\n    &.disabled {\n      a {\n        cursor: default;\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-overlay.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Plugin overlay\n */\n.ajs-plugin {\n  &.plugin-overlay {\n    display: block;\n    color: #FFF;\n    position: absolute;\n    width: 100%;\n    height: 100%;\n    z-index: 98;\n    top: 0;\n    &.eraser {\n      cursor: pointer;\n    }\n\n    .overlay-canvas {\n      /*border: 1px solid red;*/\n    }\n    .error {\n      position: absolute;\n      top: 9px;\n      left: 0;\n      width: 100%;\n      line-height: 20px;\n      color: white;\n      font-size: 20px;\n      background-color: red;\n      font-weight: bold;\n    }\n    &.draw {\n      cursor: crosshair;\n    }\n    .toolbox {\n      width: 100px;\n      position: absolute;\n      top: 0;\n      right: 0;\n      border: none;\n      padding: 0;\n      margin: 0;\n      cursor: move;\n      opacity: 0.9;\n      .add {\n        .add-ctrl,\n        .close-box,\n        .add-shape-msg,\n        .add-shape-rectangle,\n        .add-shape-ellipse {\n          height: 65px;\n          width: 101px;\n          background-color: #616161;\n          color: white;\n          font-size: 12px;\n          line-height: 20px;\n          text-align: center;\n          box-shadow: 1px 1px 0px rgb(121, 121, 121);\n          &.on {\n            background-color: #99CC00;\n          }\n        }\n        .add-shape-rectangle,\n        .add-shape-ellipse {\n          font-size: 50px;\n          line-height: 75px;\n        }\n        .add-ctrl {\n          background-color: #616161;\n          .ctrl {\n            background-color: #3cf;\n          }\n        }\n        .close-box {\n          background-color: #616161;\n          .ctrl {\n            background-color: transparent;\n            border: 3px solid white;\n          }\n        }\n      }\n      .erase-box {\n        height: 65px;\n        width: 101px;\n        background-color: rgba(49, 49, 49, 0.7);\n        color: white;\n        font-size: 12px;\n        line-height: 20px;\n        text-align: center;\n        box-shadow: 1px 1px 0px rgb(121, 121, 121);\n        &.on {\n          background-color: #99CC00;\n        }\n        .ctrl {\n          background-color: #E57373;\n        }\n      }\n      .nav-box {\n        background-color: rgba(121, 121, 121, 0.7);\n        .nav-left-box,\n        .nav-right-box {\n          display: inline-block;\n          text-align: center;\n          width: 50px;\n          height: 50px;\n          margin: 0;\n          padding: 0;\n          line-height: 50px;\n          .ctrl {\n            margin: 0;\n            padding: 0;\n            &:hover {\n              background-color: rgba(153, 204, 0, 0.7);\n            }\n          }\n        }\n      }\n      .ctrl {\n        height: 35px;\n        width: 35px;\n        display: inline-block;\n        padding: 0px;\n        font-size: 28px;\n        color: #fff;\n        text-align: center;\n        white-space: nowrap;\n        border: none;\n        border-radius: 50%;\n        cursor: pointer;\n        margin: auto;\n        position: relative;\n        vertical-align: middle;\n        box-shadow: none;\n        line-height: 46px;\n        margin: 10px 0px 0px 0px;\n        border: 3px solid transparent;\n        &.on {\n          border: 3px solid rgb(153, 204, 0);\n        }\n      }\n\n      .add-shapebox {\n        background-color: rgba(49, 49, 49, 0.7);\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-selected-item-editor.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin.plugin-selected-item-editor {\n  position: relative;\n  .auto-fill-up-form {\n    display: none;\n    height: 60px;\n    width: 700px;\n    color: #000;\n    background-color: #797979;\n    position: absolute;\n    bottom: 5px;\n    z-index: 900;\n    .message,\n    .form-item {\n      float: left;\n    }\n    .message {\n      width: 520px;\n      color: white;\n      font-size: 16px;\n      padding: 21px 0px 0px 18px;\n    }\n    .form-item {\n\n    }\n    &.on {\n      display: block;\n    }\n    &:after {\n      clear: both;\n    }\n    .form-item {\n      .form-ctrl-item-yes {\n        height: 60px;\n        width: 90px;\n        background-color: #1485cc;\n        border: none;\n        color: white;\n        font-size: 16px;\n        font-weight: bold;\n      }\n      .form-ctrl-item-no {\n        height: 60px;\n        width: 90px;\n        background-color: #0b486c;\n        border: none;\n        color: white;\n        font-size: 16px;\n        font-weight: bold;\n      }\n    }\n\n  }\n  .form {\n    height: 50px;\n    width: 100%;\n    color: #000;\n\n    .form-item {\n      float: left;\n\n      .form-item-label {\n        height: 60px;\n        background-color: #f2f2f2;\n        width: 170px;\n        border: none;\n        color: #666666;\n        font-size: 16px;\n        text-align: center;\n        font-weight: normal;\n      }\n      .form-item-tcin,\n      .form-item-tcout {\n        height: 60px;\n        width: 100px;\n        border: none;\n        color: #666666;\n        font-size: 16px;\n        text-align: center;\n        &.error {\n          background-color: #ba6868;\n        }\n      }\n      .form-ctrl-item-tcin,\n      .form-ctrl-item-tcout {\n        background-color: #1485cc;\n        border-radius: 0;\n        color: #FFFFFF;\n        display: block;\n        font-size: 16px;\n        height: 60px;\n        line-height: 20px;\n        padding: 10px;\n        text-transform: uppercase;\n        outline: none;\n        width: 70px;\n        border: none;\n        padding: 0px;\n\n      }\n      .form-ctrl-item-valid {\n        background-color: #797979;\n        height: 60px;\n        border: none;\n        color: white;\n        font-size: 16px;\n        &.valid {\n          background-color: #1485cc;\n        }\n        &:hover {\n          background-color: #6ab6e8;\n        }\n      }\n      .form-ctrl-item-delete {\n        background-color: #B71C1C;\n        height: 60px;\n        border: none;\n        color: white;\n        font-size: 16px;\n        display: none;\n        &.on {\n          display: block;\n        }\n        &:hover {\n          background-color: #C62828;\n        }\n      }\n      .form-ctrl-item-close-item {\n        background-color: #B71C1C;\n        height: 60px;\n        border: none;\n        color: white;\n        font-size: 16px;\n        display: none;\n        &.on {\n          display: block;\n        }\n        &:hover {\n          background-color: #C62828;\n        }\n      }\n      .form-ctrl-item-auto-fill-up {\n        background-color: #a1a1a1;\n        width: 60px;\n        height: 60px;\n        border: none;\n        font-size: 52px;\n        font-weight: 100;\n        padding-top: 10px;\n        color: white;\n        &:hover {\n          background-color: #6ab6e8;\n        }\n      }\n      &.delete {\n        display: none;\n        &.on {\n          display: block;\n        }\n        .form-ctrl-item-delete {\n          background-color: #22baa0;\n        }\n      }\n      &.label {\n        background-color: #f2f2f2;\n        padding: 0px;\n        border: medium none;\n        border-radius: 0;\n        box-shadow: none;\n        display: inline-block;\n        margin: 0;\n        border: none;\n      }\n    }\n  }\n  &.ajs-plugin {\n    overflow: visible !important;\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-storyboard.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin.plugin-storyboard {\n  display: none;\n  color: #FFF;\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  z-index: 98;\n  top: 0;\n  .thumbnail {\n    height: 90px;\n    width: 160px;\n    background-color: #313131;\n    background-position: 0px -90px;\n    background-repeat: no-repeat;\n    position: absolute;\n    bottom: 45px;\n    margin-top: 0px;\n    margin-right: 0px;\n    margin-bottom: 3px;\n    left: 50%;\n    border-radius: 2px;\n    border: solid #000 2px;\n    z-index: 90;\n  }\n  .filmstrip {\n    position: absolute;\n    width: 100%;\n    text-align: center;\n    margin: 0;\n    padding: 0;\n    opacity: 0.7;\n    z-index: 90;\n    -webkit-filter: grayscale(0.3);\n    -moz-filter: grayscale(0.3);\n    -ms-filter: grayscale(0.3);\n    -o-filter: grayscale(0.3);\n    .storyboard-thumbnail {\n      background-color: #313131;\n      background-position: 0px -90px;\n      background-repeat: no-repeat;\n      position: absolute;\n      margin-top: 0px;\n      margin-right: 0px;\n      margin-bottom: 3px;\n      border-radius: 2px;\n      border: solid #000 2px;\n    }\n  }\n  .preview-storyboard {\n    text-align: center;\n    display: block;\n    position: absolute;\n    margin: 0px;\n    padding: 0px;\n    .thumbnail-overlay {\n      height: 100%;\n      width: 100%;\n      position: relative;\n      border: solid #000 2px;\n      visibility: visible;\n      z-index: 80;\n      filter: blur(5px);\n      -webkit-filter: blur(3px) grayscale(0.8);\n      -moz-filter: blur(3px) grayscale(0.8);\n      -ms-filter: blur(3px) grayscale(0.8);\n      -o-filter: blur(3px) grayscale(0.8);\n    }\n  }\n\n}"
  },
  {
    "path": "src/assets/sass/_plugin-text-sync.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Plugin text sync\n */\n.ajs-plugin.plugin-text-sync {\n  background: #e6e6e6;\n  margin: 0;\n  padding: 0;\n  color: black;\n  .header {\n    overflow: hidden;\n    zoom: 1;\n    display: block;\n    margin: 0;\n    padding: 0;\n    .resume {\n      background: #555;\n      color: white;\n      padding: 15px;\n      text-align: left;\n      font-size: 14px;\n      overflow: hidden;\n      zoom: 1;\n      .heading {\n        font-size: 24px;\n        font-weight: 100;\n        color: white;\n        line-height: 1.1;\n        padding: 0;\n        margin: 0;\n      }\n    }\n  }\n\n  .line-component {\n    padding: 10px;\n    overflow-y: auto;\n    height: 100%;\n    margin: 0;\n    .line {\n\n      box-sizing: border-box;\n      color: rgb(0, 0, 0);\n      display: list-item;\n      font-size: 14px;\n      list-style-image: none;\n      list-style-position: outside;\n      list-style-type: none;\n      margin: 0;\n      overflow-x: hidden;\n      overflow-y: hidden;\n      text-align: left;\n      padding: 10px;\n\n      .info {\n        box-sizing: border-box;\n        color: rgb(0, 0, 0);\n        display: block;\n        float: left;\n        font-family: Arial, Helvetica, sans-serif;\n        font-size: 14px;\n        margin-right: 10px;\n        text-align: left;\n        .thumb {\n          background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0IwMTkyREMwMTAwMTFFNDk0QTM5OTI4MzY1OEFGQUEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0IwMTkyREQwMTAwMTFFNDk0QTM5OTI4MzY1OEFGQUEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQjAxOTJEQTAxMDAxMUU0OTRBMzk5MjgzNjU4QUZBQSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQjAxOTJEQjAxMDAxMUU0OTRBMzk5MjgzNjU4QUZBQSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Ph56Pv8AAAJASURBVHja7JnLiupAEIY1BlG8492gK1HceseFD+0juNAHcCG6GlBBBVHEG15wfk5ARDj2JHYn00zVqglN1/91qquqE2en03HIbIpDciMAAiAAAiAAApDa1J9M0jStWCz6fD4rle33+9FoNJvNPgWA+nK5bP3WYr90v+8Z2CFUKBRsjBCmdzaA3++3EYDpXTW64vF4XK1W9/tdhFyn0xmNRr1eL+dD/Hywut3u9XoVmFVUtd1u/zxhGEuj8/lcqHoY1ocXUXXgdrtZEPeGvKgOWy2fz2cyGZyrwWBwOp1EFTJBlk6nS6USBqFQCKHf7/clayXC4fBjDAb5eqH1ev0YbzYbgb2QIEO2QejjDBwOh+FwKB8A7OufUTvNyVwuVzabVRRlMplcLhfJAKC+0Wigk8E4l8v1er3z+SzNjQzq6/W6rh4WCARarZbb7ZYDQFcfi8WeH74weDyeSqWCV2Q634sCQMTXarUX9S8MUI8B0mUikcAgEon8FgCor1ar8Xj8fxN0BtijPUbL0Gw2OTIoH6pPJpPvp4Hhpbnny2AeADHNVP/m1sKLwTxAKpX68ObFhcHOSsyFwTwAl7ulzvDcV1sHMJ1Oeb0H1IdgMGh1K4FOeLlc8vreiGS13W6t7oUWi4Xt3aixEDL0ycm0GfJiDEDTNNzEUcJEbaeiYH14ERVCevX9VRca+kNDAARAAAQgGGC329moj+mdDTAej20EYHpnV2L9N63EP7p1BuZCdIgJgAAIgAAIgAD+IsC3AAMAzUKl+/tbh2cAAAAASUVORK5CYII=');\n          cursor: pointer;\n          width: 64px;\n          background-size: cover;\n          height: 64px;\n        }\n\n        .badge {\n          border-radius: 10px;\n          border: none;\n          cursor: pointer;\n          display: inline-block;\n          min-width: 10px;\n          padding: 3px 7px;\n          font-size: 12px;\n          font-weight: 700;\n          color: #fff;\n          line-height: 1;\n          vertical-align: baseline;\n          white-space: nowrap;\n          text-align: center;\n          background-color: #999;\n          &.tcin {\n            margin: 4px 0;\n          }\n\n          &.tcout {\n            margin: 0 0 4px 0;\n          }\n          &.tcin,\n          &.tcout {\n            clear: both;\n            float: left;\n            border-radius: 3px;\n          }\n        }\n      }\n      &.on {\n        background-color: #d8d8d8;\n        .content {\n          .ajs-progress .ajs-progress-bar {\n            background-color: $mainColor;\n          }\n        }\n      }\n      &.on .info .badge.tcin,\n      &.on .info .badge.tcout {\n        background-color: $mainColor;\n        cursor: pointer;\n      }\n      .content {\n        overflow: hidden;\n        zoom: 1;\n        text-align: left;\n        font-size: 14px;\n\n        .heading {\n          text-align: left;\n          font-size: 14px;\n        }\n        .text {\n          text-align: left;\n          font-size: 14px;\n          .word {\n            cursor: pointer;\n            &.on {\n              font-weight: bold;\n            }\n          }\n        }\n        .ajs-progress {\n          border-radius: 3px;\n          width: 100%;\n          height: 2px;\n          .ajs-progress-bar {\n            background-color: #d8d8d8;\n            height: 1px;\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-timeline.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n* Plugin time line\n*/\n$modulesSizeListNames: lg, md, sm, xs;\n$modulesSizeList: 110px, 80px, 60px, 25px;\n\n.ajs-plugin.plugin-timeline {\n  position: relative;\n  background-color: #000;\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n\n  .timeline-cursor {\n    border-left: 1px solid $mainColor;\n    display: block;\n    height: 100%;\n    width: 1px;\n    left: 0%;\n    position: absolute;\n    z-index: 100;\n    bottom: 30px;\n  }\n\n  .timeline-progress-container {\n    position: absolute;\n    width: 100%;\n    height: 102px;\n    background-color: $mainBackgroundColor;\n    z-index: 90;\n    right: 100%;\n  }\n\n  .timeaxis {\n    /* tools bar */\n    .toolsbar {\n      background-color: #3f3f3f;\n      padding: 5px 10px 0 10px;\n      height: 40px;\n      .ajs-row {\n        .ajs-col {\n          float: left;\n          position: relative;\n          min-height: 1px;\n          padding: 0px;\n          margin: 0px;\n          &:after {\n            clear: both;\n          }\n        }\n        .leftContainer {\n          width: 16.66666667%;\n          text-align: left;\n        }\n        .middleContainer {\n          width: 66.66666667%;\n          text-align: center;\n        }\n        .rightContainer {\n          width: 16.66666667%;\n          text-align: right;\n        }\n      }\n      button.ajs-icon {\n        display: inline-block;\n        color: white;\n        background-color: #333;\n        border-radius: 50%;\n        border: none;\n        box-shadow: none;\n        padding: 0;\n        width: 30px;\n        height: 30px;\n        padding: 0 2px 0 1px;\n        line-height: 36px;\n        font-size: 20px;\n        &:hover {\n          background-color: $mainColor;\n        }\n      }\n    }\n    /**Module TimeAxe**/\n    .module-timeaxis {\n      position: relative;\n      background-color: #333;\n      height: 100px;\n      margin-top: 2px;\n      .line {\n        height: 20px;\n        position: absolute;\n        top: 30px;\n        width: 100%;\n        margin-top: 5px;\n        z-index: 99;\n        border-bottom: 1px solid white;\n      }\n\n      .line-content {\n        text-align: center;\n        position: absolute;\n        top: 50%;\n        width: 100%;\n        height: auto;\n        z-index: 99;\n      }\n\n      .time-grid {\n        height: 10px;\n        width: 1px;\n        padding-top: 2px;\n        display: block;\n        position: absolute;\n        border-right: 1px solid #FFF;\n      }\n\n      .time-grid span.time {\n        display: block;\n        width: 55px;\n        font-size: 12px;\n        position: relative;\n        top: -20px;\n        line-height: 15px;\n        left: 50%;\n        margin-left: -27px;\n        cursor: pointer;\n      }\n    }\n\n    .timeaxis-label {\n      display: none;\n    }\n    .toolbar-container {\n      position: absolute;\n      top: 0;\n      right: 20px;\n      .plugin-btn {\n        border: none;\n        box-shadow: none;\n        background: none;\n        color: #6c6c6c;\n        font-size: 17px;\n        line-height: 25px;\n      }\n    }\n    &.off {\n      height: 25px;\n      background-color: #333;\n      .toolsbar {\n        display: none;\n      }\n      .module-timeaxis {\n        display: none;\n      }\n      .timeaxis-label {\n        position: absolute;\n        top: 0;\n        display: block;\n        .label {\n          font-size: 11px;\n          padding: 0px 0px 0px 5px;\n          margin: 0;\n        }\n      }\n    }\n\n  }\n\n  /**Composants du timeline**/\n  .components {\n    display: block;\n    position: relative;\n    width: 100%;\n    overflow: hidden;\n\n    .cuepoints-component,\n    .segments-component,\n    .images-component,\n    .histogram-component,\n    .visual-component {\n      position: relative;\n      cursor: pointer;\n    }\n\n    .cuepoints-component .module-cuepoints,\n    .segments-component .module-segments,\n    .images-component .module-images,\n    .histogram-component .module-histogram,\n    .visual-component .module-visual {\n      position: relative;\n      background-color: #333;\n      height: 100px;\n      margin-top: 2px;\n      text-align: left;\n    }\n    .cuepoints-component.activated .module-cuepoints,\n    .segments-component.activated .module-segments,\n    .images-component.activated .module-images,\n    .histogram-component.activated .module-histogram,\n    .visual-component.activated .module-visual {\n      background-color: #3f3f3f;\n    }\n\n    .cuepoints-component .module-cuepoints .line-content,\n    .segments-component .module-segments .line-content,\n    .images-component .module-images .line-content,\n    .visual-component .module-visual .line-content {\n      position: absolute;\n      top: 50%;\n      width: 100%;\n      height: auto;\n      z-index: 99;\n    }\n\n    @mixin generate-theme($n, $i: 1) {\n      @for $i from 1 through 4 {\n        $sizeName: nth($modulesSizeListNames, $i);\n        .component.cuepoints-component.#{$sizeName} .module-cuepoints,\n        .component.segments-component.#{$sizeName} .module-segments,\n        .component.images-component.#{$sizeName} .module-images,\n        .component.histogram-component.#{$sizeName} .module-histogram,\n        .component.visual-component.#{$sizeName} .module-visual {\n          height: nth($modulesSizeList, $i);\n        }\n\n        .#{sizeName} .focus-container {\n          .ui-resizable-handle {\n            height: nth($modulesSizeList, $i);\n          }\n        }\n      }\n    }\n\n    @include generate-theme(4);\n    $sizeName: nth($modulesSizeListNames, 4);\n\n    .cuepoints-component.#{$sizeName} .module-cuepoints .line-content,\n    .segments-component.#{$sizeName} .module-segments .line-content,\n    .images-component.#{$sizeName} .module-images .line-content,\n    .histogram-component.#{$sizeName} .module-histogram .line-content,\n    .visual-component.#{$sizeName} .module-visual .line-content {\n      display: none;\n    }\n    .component.#{$sizeName} .sub,\n    .component.#{$sizeName} .bottom-toolbar-container,\n    .component.#{$sizeName} .focus-container {\n      display: none;\n    }\n    .cuepoints-component .module-cuepoints .line,\n    .segments-component .module-segments .line,\n    .images-component .module-images .line,\n    .histogram-component .module-images .line,\n    .visual-component .module-visual .line {\n      background-color: #fff;\n      height: 1px;\n      position: absolute;\n      z-index: 98;\n      top: 2px;\n      width: 100%;\n    }\n\n    .cuepoints-component .module-cuepoints .cuepoint {\n      position: absolute;\n      vertical-align: middle;\n      cursor: pointer;\n      font-size: 20px;\n      line-height: 13px;\n      z-index: 98;\n      margin-left: -10px;\n      &.selected {\n        animation-name: ajs-bounce;\n        -webkit-animation-name: ajs-bounce;\n        animation-duration: 1.6s;\n        -webkit-animation-duration: 1.6s;\n        animation-timing-function: ease;\n        -webkit-animation-timing-function: ease;\n        transform-origin: 50% 100%;\n        -ms-transform-origin: 50% 100%;\n        -webkit-transform-origin: 50% 100%;\n        animation-iteration-count: infinite;\n        -webkit-animation-iteration-count: infinite;\n      }\n    }\n\n    .segments-component .module-segments .segment {\n      position: absolute;\n      height: 7px;\n      width: 1px;\n      float: left;\n      z-index: 99;\n      background-color: $mainColor;\n      cursor: pointer;\n      border: none;\n      margin: 0;\n      padding: 0;\n      .ui-resizable-e {\n        cursor: e-resize;\n        width: 7px;\n        right: -5px;\n        top: 0;\n        height: 100%;\n      }\n      .ui-resizable-w {\n        cursor: w-resize;\n        width: 7px;\n        left: -5px;\n        top: 0;\n        height: 100%;\n      }\n      .ui-resizable-handle {\n        position: absolute;\n        font-size: .1px;\n        z-index: 99999;\n        display: block;\n      }\n      &.selected {\n        /**hatch**/\n        animation-name: ajs-pulse;\n        -webkit-animation-name: ajs-pulse;\n        animation-duration: 2s;\n        -webkit-animation-duration: 2s;\n        animation-timing-function: ease-in-out;\n        -webkit-animation-timing-function: ease-in-out;\n        transform-origin: 50% 100%;\n        -ms-transform-origin: 50% 100%;\n        -webkit-transform-origin: 50% 100%;\n        animation-iteration-count: infinite;\n        -webkit-animation-iteration-count: infinite;\n        visibility: visible !important;\n      }\n      &.marker {\n        height: 2px;\n        margin: 0px;\n        padding: 0px;\n        top: 2px;\n        .ui-resizable-w {\n          cursor: w-resize;\n          width: 7px;\n          left: -5px;\n          top: -3px;\n          height: 8px;\n          background-color: inherit;\n        }\n        .ui-resizable-e {\n          cursor: e-resize;\n          width: 7px;\n          right: -5px;\n          top: -3px;\n          height: 8px;\n          background-color: inherit;\n        }\n      }\n\n    }\n    .visual-component .module-visual {\n      .cuepoint {\n        position: absolute;\n        vertical-align: middle;\n        cursor: pointer;\n        font-size: 20px;\n        line-height: 13px;\n        z-index: 98;\n        margin-left: -10px;\n        top: -2px;\n        &.selected {\n          animation-name: ajs-bounce;\n          -webkit-animation-name: ajs-bounce;\n          animation-duration: 1.6s;\n          -webkit-animation-duration: 1.6s;\n          animation-timing-function: ease;\n          -webkit-animation-timing-function: ease;\n          transform-origin: 50% 100%;\n          -ms-transform-origin: 50% 100%;\n          -webkit-transform-origin: 50% 100%;\n          animation-iteration-count: infinite;\n          -webkit-animation-iteration-count: infinite;\n        }\n      }\n      .segment {\n        position: absolute;\n        height: 7px;\n        width: 1px;\n        float: left;\n        z-index: 99;\n        background-color: $mainColor;\n        cursor: pointer;\n        .cuepoint {\n          top: -7px;\n          margin-right: -10px;\n        }\n        &.selected {\n          /**hatch**/\n          animation-name: ajs-hatch;\n          -webkit-animation-name: ajs-hatch;\n          animation-duration: 2s;\n          -webkit-animation-duration: 2s;\n          animation-timing-function: ease-in-out;\n          -webkit-animation-timing-function: ease-in-out;\n          transform-origin: 50% 100%;\n          -ms-transform-origin: 50% 100%;\n          -webkit-transform-origin: 50% 100%;\n          animation-iteration-count: infinite;\n          -webkit-animation-iteration-count: infinite;\n          visibility: visible !important;\n\n        }\n        &.marker {\n          height: 2px;\n          margin: 0px;\n          padding: 0px;\n          top: 2px;\n          .ui-resizable-w {\n            cursor: w-resize;\n            width: 15px;\n            left: 0px;\n            top: -6px;\n            height: 15px;\n            background-color: inherit;\n            position: absolute;\n          }\n          .ui-resizable-e {\n            cursor: e-resize;\n            width: 15px;\n            right: 0px;\n            top: -6px;\n            height: 15px;\n            background-color: inherit;\n            position: absolute;\n          }\n        }\n      }\n    }\n    .images-component .module-images .image {\n      position: absolute;\n      float: left;\n      z-index: 99;\n      background-color: none;\n      height: 50px;\n      top: -25px;\n      background-repeat: no-repeat;\n      line-height: 50px;\n      cursor: pointer;\n      &.on {\n        z-index: 100;\n        .content {\n        }\n        &.segment {\n          z-index: 100;\n          border: solid 1px $mainColor;\n          margin-top: -1px;\n          .content {\n            /*height: 45px;*/\n          }\n        }\n      }\n\n      .content {\n        height: 30px;\n        z-index: 1;\n        position: absolute;\n        top: 15px;\n      }\n\n      hr.flow {\n        border-color: #FFF;\n        color: #FFF;\n        display: block;\n        margin: 0;\n        padding: 0;\n        position: relative;\n        height: 0px;\n        top: 28px;\n        z-index: 0;\n        border-style: double;\n        border-width: 1px;\n        width: 100%;\n        &:after,\n        &:before {\n\n          display: inline-block;\n          font-family: FontAwesome;\n          font-size: 1.1em;\n          position: absolute;\n          top: -25px;\n        }\n        &:after {\n          right: -6px;\n          content: \"\\f111\";\n        }\n\n        &:before {\n          left: -3px;\n          content: \"\\f142\";\n        }\n      }\n    }\n    .histogram-component .module-histogram .cuepoint {\n      position: absolute;\n      vertical-align: middle;\n      cursor: pointer;\n      font-size: 16px;\n      line-height: 8px;\n      z-index: 98;\n      margin-left: -5px;\n    }\n    .histogram-component .module-histogram .label {\n      display: block;\n      position: absolute;\n      top: 0px;\n    }\n    .histogram-component .module-histogram .line-content {\n      position: relative;\n      height: 100%;\n    }\n\n    .cuepoints-component .module-cuepoints .label,\n    .segments-component .module-segments .label,\n    .images-component .module-images .label,\n    .histogram-component .module-histogram .label,\n    .visual-component .module-visual .label {\n      background-color: transparent;\n      -webkit-touch-callout: text;\n      -webkit-user-select: text;\n      -khtml-user-select: text;\n      -moz-user-select: text;\n      -ms-user-select: text;\n      user-select: text;\n      padding: 0px 0px 0px 5px;\n      margin: 0;\n      display: inline-block;\n    }\n    .cuepoints-component .module-cuepoints .callback,\n    .segments-component .module-segments .callback,\n    .images-component .module-images .callback,\n    .histogram-component .module-histogram .callback,\n    .visual-component .module-visual .callback {\n      background-color: transparent;\n      -webkit-touch-callout: text;\n      -webkit-user-select: text;\n      -khtml-user-select: text;\n      -moz-user-select: text;\n      -ms-user-select: text;\n      user-select: text;\n      padding: 0px 0px 0px 5px;\n      margin: 0;\n      display: inline-block;\n    }\n\n    .sub.cuepoints-component .module-cuepoints,\n    .sub.segments-component .module-segments,\n    .sub.images-component .module-images,\n    .sub.images-histogram .module-histogram,\n    .sub.visual-histogram .module-visual {\n      background-color: rgba(100, 100, 100, 0.68);\n    }\n    .focus-container {\n      left: 0%;\n      width: 222px;\n      display: block;\n      background-color: rgba(100, 100, 100, 0.68);\n      height: 50%;\n      border: 0px solid #333;\n      box-shadow: none;\n      position: absolute;\n      top: 0px;\n      display: block;\n      z-index: 100;\n      min-width: 41px;\n      &.zoom {\n        background-color: $mainBackgroundColor;\n        height: 100%;\n      }\n      .ui-resizable-handle {\n        background-color: #6c6c6c;\n        width: 20px;\n        height: 100%;\n        text-align: center;\n        vertical-align: middle;\n        display: inline-block;\n        margin-left: 0px;\n        margin-right: 0px;\n        &.ui-resizable-w {\n          float: left;\n          font-size: 20px;\n          position: relative;\n          &:before {\n            -webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\n            -ms-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\n            transform: translateX(-50%) translateY(-50%) rotate(-90deg);\n            display: block;\n            width: 20px;\n            height: 20px;\n            margin: 2px 0px 0px 12px;\n            padding: 0px;\n            position: absolute;\n            top: 50%;\n          }\n        }\n        &.ui-resizable-e {\n          float: right;\n          font-size: 20px;\n          &:before {\n            -webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\n            -ms-transform: translateX(-50%) translateY(-50%) rotate(-90deg);\n            transform: translateX(-50%) translateY(-50%) rotate(-90deg);\n            display: block;\n            width: 20px;\n            height: 20px;\n            margin: 2px 0px 0px 12px;\n            padding: 0px;\n            position: absolute;\n            top: 50%;\n          }\n        }\n      }\n    }\n    .bottom-toolbar-container {\n      position: absolute;\n      bottom: 5px;\n      left: 5px;\n      display: block;\n      .focus-btn {\n        background-color: transparent;\n        border: none;\n        color: white;\n        font-size: 20px;\n        line-height: 20px;\n      }\n    }\n    .component.focus-off {\n      .focus-container {\n        display: none;\n      }\n      .bottom-toolbar-container {\n        display: block;\n      }\n      .sub {\n        display: none;\n      }\n    }\n    .component.focus-on {\n      .focus-container {\n        display: block;\n      }\n      .bottom-toolbar-container {\n        display: none;\n      }\n      .sub {\n        display: block;\n      }\n    }\n\n    $smallSizeName: extract($modulesSizeListNames, 4);\n\n    .component.#{$smallSizeName} .sub,\n    .component.#{$smallSizeName} .bottom-toolbar-container,\n    .component.#{$smallSizeName} .focus-container {\n      display: none;\n    }\n\n    .timecursor {\n      left: 0%;\n      position: absolute;\n      top: 0px;\n      width: 1px;\n      border-left: 1px solid $mainColor;\n      height: 100%;\n    }\n\n    .toolbar-container {\n      position: absolute;\n      top: 0;\n      right: 20px;\n\n      .plugin-btn {\n        border: none;\n        box-shadow: none;\n        background: none;\n        color: #6c6c6c;\n        font-size: 17px;\n        line-height: 25px;\n      }\n\n      .close-btn,\n      .sort-btn,\n      .expand-btn {\n        color: $BtToolContColor;\n        height: 28px;\n        line-height: 28px;\n        text-align: center;\n        width: 28px;\n        cursor: pointer;\n        border: none;\n        background: transparent;\n        font-size: 20px;\n      }\n      .nav-controls {\n        color: $BtToolContColor;\n        display: inline-block;\n        float: left;\n        line-height: 28px;\n        margin-right: 15px;\n        text-align: center;\n        vertical-align: middle;\n        font-size: 20px;\n        padding: 0;\n        .prev-control {\n          cursor: pointer;\n          border: none;\n          background: transparent;\n          color: $BtToolContColor;\n          font-size: 20px;\n          padding: 0;\n        }\n        .next-control {\n          cursor: pointer;\n          border: none;\n          background: transparent;\n          color: $BtToolContColor;\n          font-size: 20px;\n          padding: 0;\n        }\n      }\n    }\n  }\n\n  /**Bar de controle**/\n  .module-nav-bar-container {\n    .info {\n      padding-left: 15px;\n      line-height: 30px;\n    }\n    .toolsbar {\n      height: 30px;\n      padding: 0px;\n      background-color: #3f3f3f;\n      button.ajs-icon {\n        border-radius: 0%;\n        width: 30px;\n        line-height: 25px;\n        border: none;\n        background-color: transparent;\n        color: white;\n      }\n    }\n    .leftContainer {\n      text-align: left;\n      border: none;\n      background-color: transparent;\n      float: left;\n      width: 16.66666667%;\n\n    }\n    .middleContainer {\n      text-align: center;\n      border: none;\n      background-color: transparent;\n      float: left;\n      width: 66.66666667%;\n\n    }\n\n    .rightContainer {\n      text-align: right;\n      border: none;\n      background-color: transparent;\n      float: right;\n      width: 16.66666667%;\n      .config-menu {\n        padding-right: 30px;\n        .config-btn {\n          width: 30px;\n          line-height: 29px;\n          cursor: pointer;\n          font-size: 2.4em;\n\n        }\n      }\n      .config-menu-list {\n        background: #FFF;\n        border: none;\n        box-shadow: none;\n        margin: 0px;\n        position: absolute;\n        bottom: 30px;\n        border-radius: 0px;\n        right: 1px;\n        width: 200px;\n        list-style: none;\n        padding: 5px 0px 5px 5px;\n        li {\n          padding-left: 5px;\n          color: #000;\n          cursor: pointer;\n          text-align: left;\n        }\n      }\n    }\n    &:after {\n      clear: both;\n    }\n\n  }\n  /**resizeable component**/\n  .ui-resizable-s {\n    display: block;\n    height: 30px;\n    width: 30px;\n    font-size: 30px;\n    position: absolute;\n    left: 100%;\n    bottom: 0;\n    margin-left: -45px;\n    padding-left: 15px;\n    display: inline-block;\n    vertical-align: middle;\n    line-height: 40px;\n    font-weight: normal;\n    font-style: normal;\n    cursor: s-resize;\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin-watermark.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin.plugin-watermark {\n  display: block;\n  color: #FFF;\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  z-index: 98;\n  top: 0;\n  .watermark {\n    background-color: transparent;\n    background-repeat: no-repeat;\n    position: absolute;\n  }\n}"
  },
  {
    "path": "src/assets/sass/_plugin.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Plugin\n */\n\n.ajs-plugin {\n  font-family: $fontMain;\n  color: #FFF;\n  clear: both;\n  overflow: hidden;\n  font-size: 11px;\n\n  .error {\n    text-align: center;\n  }\n\n  .loader {\n    text-align: center;\n    width: 100%;\n    top: 50%;\n    font-size: 3em;\n  }\n\n  /* Custom de la scrollbar */\n  &::-webkit-scrollbar,\n  & ::-webkit-scrollbar {\n    width: 10px;\n  }\n  &::-webkit-scrollbar-thumb,\n  & ::-webkit-scrollbar-thumb {\n    background: rgba(108, 108, 108, .6);\n  }\n}\n\n@import\"_player-custom-control-bar\";\n@import\"_plugin-timeline\";\n@import\"_plugin-text-sync\";\n@import\"_plugin-captions\";\n@import\"_plugin-overlay\";\n@import\"_plugin-watermark\";\n@import\"_plugin-menu-contextuel\";\n@import\"_plugin-editor\";\n@import\"_plugin-selected-item-editor\";\n@import\"_plugin-storyboard\";\n@import\"_plugin_d3js_chart\";\n"
  },
  {
    "path": "src/assets/sass/_plugin_d3js_chart.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n.ajs-plugin .plugin-db3-js-chart {\n  border-top: solid 2px $mainBackgroundColor;\n  margin-top: -13px;\n  .components {\n    .component {\n      position: relative;\n      .timeline-cursor {\n        left: 0px;\n        width: 1px;\n        height: 100%;\n        display: block;\n        background-color: $mainColor;\n        position: absolute;\n        z-index: 100;\n        bottom: 25px;\n        margin-left: -3px;\n      }\n      // Background box\n      .module-chart {\n        background-color: $backgroundColor;\n        display: block;\n        width: 100%;\n        height: 200px;\n      }\n\n      // grid-line cursor\n      .line {\n        background-color: $inverseBackgroundColor;\n        display: block;\n        width: 100%;\n        height: 3px;\n        top: 12px;\n        position: relative;\n      }\n\n      // Background Cursor\n      .focus-container {\n        display: block;\n        width: 100%;\n        height: 25px;\n        background-color: lighten($secondaryBackgroundColor, 10%);\n        margin-top: -3px;\n\n        // Cursor middle segment\n        .focus-component {\n          background-color: $mainColor;\n          cursor: e-resize;\n          display: block;\n          height: 3px;\n          position: relative;\n          top: 12px;\n\n          // Left and Right handle cursor\n          .ui-resizable-handle.ui-resizable-w.ajs-icon,\n          .ui-resizable-handle.ui-resizable-e.ajs-icon {\n            border-radius: 30%;\n            cursor: pointer;\n            height: 15px;\n            width: 15px;\n            display: block;\n            background-color: white;\n            top: -7px;\n\n            // Add grid indicator\n            &:before {\n              content: \"\";\n              display: block;\n              border-left: dashed 1px $mainColor;\n              height: 173px;\n              position: absolute;\n              top: -175px;\n              left: 7px;\n            }\n          }\n        }\n      }\n      // Override SVG color\n      svg.nvd3-svg {\n\n        .tick {\n          opacity: .6;\n\n          text {\n            fill: white;\n          }\n        }\n\n        .nv-legend-text {\n          fill: white;\n          font-size: 14px;\n        }\n      }\n    }\n  }\n\n}"
  },
  {
    "path": "src/assets/sass/main.scss",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**************************************************\n****************** *****VARIABLES *****************\n**************************************************/\n/***** FONTS ****/\n$fontMain: Arial, Helvetica, sans-serif;\n/***** COLORS ****/\n$BtToolContColor : #6c6c6c;\n\n/**************************************************\n****************** THEMES PROJETS *****************\n**************************************************/\n/***** PLAYER PROJET GEN ****/\n$mainColor: #0CF;\n$primaryColor: #0d8bc7;\n$secondaryColor: #4cd1d5;\n$mainBackgroundColor: rgba(19, 231, 240, 0.68);\n$mainGradient: linear-gradient(to right, #13e7f0, #0c7abf);\n$inverseBackgroundColor: rgba(51, 51, 51, 0.68);\n\n$backgroundColor : #2e2e2e;\n$primaryBackgroundColor : #292929;\n$secondaryBackgroundColor : #393939;\n\n// Devices width\n$screen-xx-min: 320px;\n$screen-xs-min: 480px;\n$screen-sm-min: 768px;\n$screen-md-min: 992px;\n$screen-lg-min: 1200px;\n\n\n@import \"_ajs-animations\";\n@import \"_ajs-mixins\";\n@import \"_ajs-webfont\";\n@import \"_default-style\";\n\n@import \"_player\";\n@import \"_plugin\";\n\n// test"
  },
  {
    "path": "src/helpers/browser-feature-detection.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * This class provides borwser utility functions.\r\n * @class BrowserFeatureDetection\r\n * @namespace fr.ina.amalia.player.helpers\r\n * @module player-utils\r\n * @constructor\r\n * @param {Object} settings Defines the configuration of this class\r\n */\r\n$.Class(\"fr.ina.amalia.player.helpers.BrowserFeatureDetection\", {\r\n        /**\r\n         * True if mobile device\r\n         * @method isMobilePlatform\r\n         * @return {Array}\r\n         */\r\n        isMobilePlatform: function () {\r\n            return (navigator.userAgent.match(/(iPod|iPhone|iPad)/) || navigator.userAgent.toLowerCase().indexOf('android') > -1);\r\n        }\r\n    },\r\n    {\r\n        /**\r\n         * Defines configuration\r\n         * @property settings\r\n         * @type {Object}\r\n         * @default {}\r\n         */\r\n        settings: {},\r\n        /**\r\n         * In charge to render messages in the web console output\r\n         * @property logger\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        logger: null,\r\n        /**\r\n         * Init this class\r\n         * @constructor\r\n         * @method init\r\n         * @param {Object} settings\r\n         */\r\n        init: function (settings) {\r\n            this.settings = $.extend({\r\n                    debug: false\r\n                },\r\n                settings || {});\r\n            if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\r\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\r\n                    enabled: this.settings.debug\r\n                });\r\n            }\r\n        },\r\n        /**\r\n         * Method checks if the browser can play the specified audio/video\r\n         * @method isSupportsVideos\r\n         * @returns {Boolean} Return false if browser don't support this audio/video\r\n         * file.\r\n         */\r\n        isSupportsVideos: function () {\r\n            if (!!document.createElement('video').canPlayType) {\r\n                if (this.logger !== null) {\r\n                    this.logger.info(\"Support html5\");\r\n                }\r\n                return true;\r\n            }\r\n            else {\r\n                if (this.logger !== null) {\r\n                    this.logger.info(\"Can't support html5\");\r\n                }\r\n                return false;\r\n            }\r\n        },\r\n        /**\r\n         * Method checks if the browser can play the specified mp4 file.\r\n         * @method isSupportsMp4\r\n         * @returns {Boolean} Return false if browser don't this audio/video file.\r\n         */\r\n        isSupportsMp4: function () {\r\n            if (!this.isSupportsVideos()) {\r\n                return false;\r\n            }\r\n            else {\r\n                var video = document.createElement(\"video\");\r\n                if (video.canPlayType('video/mp4')) {\r\n                    if (this.logger !== null) {\r\n                        this.logger.info(\"Support mp4\");\r\n                    }\r\n                    return true;\r\n                }\r\n                else {\r\n                    if (this.logger !== null) {\r\n                        this.logger.info(\"Can't support mp4\");\r\n                    }\r\n                    return false;\r\n                }\r\n            }\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/helpers/html5-helper.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * This class provides html5 utility functions\n * @class HTML5Helper\n * @namespace fr.ina.amalia.player.helpers\n * @module player-utils\n */\n$.Class(\"fr.ina.amalia.player.helpers.HTML5Helper\", {\n        /**\n         * Method checks whether the document is in full-screen mode.\n         * @method inFullScreen\n         * @returns {Boolean} Return true if the document is in full-screen mode.\n         */\n        inFullScreen: function () {\n            if (\n                document.fullscreenEnabled ||\n                document.webkitFullscreenEnabled ||\n                document.mozFullScreenEnabled ||\n                document.msFullscreenEnabled\n            ) {\n                if (\n                    document.fullscreenElement ||\n                    document.webkitFullscreenElement ||\n                    document.mozFullScreenElement ||\n                    document.msFullscreenElement\n                ) {\n                    return true;\n                }\n            }\n\n                return false;\n\n        },\n        /**\n         * Method in charge to toggle full-screen mode\n         * @method toggleFullScreen\n         * @param dom container\n         * @param force force to call full-screen mode\n         * @return {Boolean} Return true if in full-screen mode.\n         */\n        toggleFullScreen: function (dom, force) {\n            var inFullScreen = fr.ina.amalia.player.helpers.HTML5Helper.inFullScreen();\n            if (inFullScreen === false || force === true) {\n                var requestFullScreen = dom.requestFullScreen || dom.webkitRequestFullScreen || dom.mozRequestFullScreen || dom.msRequestFullscreen || dom.webkitEnterFullscreen;\n                if (typeof requestFullScreen !== \"undefined\" && requestFullScreen) {\n                    requestFullScreen.call(dom);\n                    return true;\n                }\n            }\n            else {\n                var cancelFullScreen = document.cancelFullScreen || document.mozCancelFullScreen || document.webkitCancelFullScreen || document.exitFullscreen || document.msExitFullscreen;\n                if (typeof cancelFullScreen !== \"undefined\" && cancelFullScreen) {\n                    cancelFullScreen.call(document);\n                    return false;\n                }\n            }\n        }\n    },\n    {});\n"
  },
  {
    "path": "src/helpers/utilities-helper.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * This class provides some utility functions.\r\n * @class UtilitiesHelper\r\n * @namespace fr.ina.amalia.player.helpers\r\n * @module player-utils\r\n */\r\n$.Class(\"fr.ina.amalia.player.helpers.UtilitiesHelper\", {\r\n        /**\r\n         * Method in charge to format time\r\n         * @method formatTime\r\n         * @param seconds\r\n         * @param defaultFps frames per second\r\n         * @param format Format specifier h/m/s/f/ms/mms\r\n         * @return {String} return format time\r\n         */\r\n        formatTime: function (seconds, defaultFps, format) {\r\n            var formatTime = formatTime || \"f\";\r\n            var FPS = defaultFps || 25;\r\n            var _fps = Math.floor((Math.floor((seconds - Math.floor(seconds)) * 100000) * FPS) / 100000);\r\n            var minutes = Math.floor(seconds / 60);\r\n            var hours = Math.floor(minutes / 60);\r\n            var milliseconds = seconds % 60;\r\n            seconds = Math.floor(seconds % 60);\r\n            minutes = Math.floor(minutes % 60);\r\n            minutes = (minutes >= 10) ? minutes : \"0\" + minutes;\r\n            hours = (hours >= 10) ? hours : \"0\" + hours;\r\n            seconds = (seconds >= 10) ? seconds : \"0\" + seconds;\r\n            _fps = (_fps >= 10) ? _fps : \"0\" + _fps;\r\n\r\n            switch (format) {\r\n                case 'h' :\r\n                    formatTime = hours;\r\n                    break;\r\n                case 'm' :\r\n                    formatTime = hours + \":\" + minutes;\r\n                    break;\r\n                case 's' :\r\n                    formatTime = hours + \":\" + minutes + \":\" + seconds;\r\n                    break;\r\n                case 'seconds' :\r\n                    formatTime = parseFloat(parseFloat(hours) * 3600 + parseFloat(minutes) * 60 + parseFloat(milliseconds)).toFixed(4);\r\n                    break;\r\n                case 'mms' :\r\n                    formatTime = hours + \":\" + minutes + \":\" + seconds + \".\" + milliseconds.toFixed(4).split('.')[1];\r\n                    break;\r\n                case 'ms' :\r\n                    formatTime = hours + \":\" + minutes + \":\" + seconds + \".\" + milliseconds.toFixed(2).split('.')[1];\r\n                    break;\r\n                case 'f' :\r\n                    formatTime = hours + \":\" + minutes + \":\" + seconds + \":\" + _fps;\r\n                    break;\r\n                default :\r\n                    formatTime = hours + \":\" + minutes + \":\" + seconds + \":\" + milliseconds.toFixed(2).split('.')[1];\r\n            }\r\n            return formatTime;\r\n        },\r\n        /**\r\n         * Method to get time range by interval\r\n         * @method getTimeRange\r\n         * @param duration duration in second\r\n         * @param interval\r\n         * @param defaultFps frames per second\r\n         */\r\n        getTimeRange: function (duration, interval, defaultFps) {\r\n            var ranges = [];\r\n            var FPS = defaultFps || 25;\r\n            var _interval = (typeof interval === \"undefined\") ? \"1\" : interval;\r\n            duration = Math.floor(duration);\r\n            for (var start = 0;\r\n                 start <= duration;\r\n                 start += _interval) {\r\n                if (interval === 'f') {\r\n                    start = Math.floor(start);\r\n                    for (var iFps = 0;\r\n                         iFps < FPS;\r\n                         iFps++) {\r\n                        start += (1000 / FPS) / 1000;\r\n                        ranges.push(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(start, defaultFps));\r\n                    }\r\n                }\r\n                else {\r\n                    ranges.push(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(start, defaultFps));\r\n                }\r\n            }\r\n            return ranges;\r\n        },\r\n        /**\r\n         * Method in charge to return range\r\n         * @param {Number} startTime\r\n         * @param {Number} endTime\r\n         * @param {Number} interval\r\n         * @return {Array}\r\n         */\r\n        getRangesWithStartAndEndTime: function (startTime, endTime, interval) {\r\n            var ranges = [];\r\n            var _interval = (typeof interval === \"undefined\") ? \"1\" : interval;\r\n            for (var start = startTime;\r\n                 start <= endTime;\r\n                 start += _interval) {\r\n                ranges.push(start);\r\n            }\r\n            return ranges;\r\n        },\r\n        /**\r\n         * Converts the specified hour to seconds.\r\n         * @method convertHourToSeconde\r\n         * @param _time format HH:MM:SS\r\n         */\r\n        convertHourToSeconde: function (_time) {\r\n            var time = null;\r\n            // regex patter to search on\r\n            var patt = /\\d{2}:\\d{2}:\\d{2}/;\r\n            // return the matching date string\r\n            var result = patt.exec(_time);\r\n            if (result !== null) {\r\n                result = _time.split(':');\r\n                var hours = Math.floor(result[0]);\r\n                var minutes = Math.floor(result[1]);\r\n                var seconds = parseFloat(result[2]);\r\n                time = (hours * 60 * 60) + (minutes * 60) + seconds;\r\n            }\r\n            return time;\r\n        },\r\n        /**\r\n         * Converts the specified hour to seconds.\r\n         * @method convertTimeFPSToSeconde\r\n         * @param _time format HH:MM:SS:FF\r\n         * @param _fps fps\r\n         */\r\n        convertTimeFPSToSeconde: function (_time, _fps) {\r\n            var time = null;\r\n            var FPS = _fps || 25;\r\n            // regex patter to search on\r\n            var patt = /\\d{2}:\\d{2}:\\d{2}:\\d{2}/;\r\n            // return the matching date string\r\n            var result = patt.exec(_time);\r\n            if (result !== null) {\r\n                result = _time.split(':');\r\n                var hours = Math.floor(result[0]);\r\n                var minutes = Math.floor(result[1]);\r\n                var seconds = parseFloat(result[2]);\r\n                var f = parseFloat(result[3]);\r\n                time = (hours * 60 * 60) + (minutes * 60) + seconds + ((f * 1000 / FPS) / 1000);\r\n            }\r\n            return time;\r\n        },\r\n        /**\r\n         * Convert Base64 string to array encoding\r\n         * @method uint6ToB64\r\n         * @param {String} nUint6\r\n         * @return {Number}\r\n         */\r\n        uint6ToB64: function (nUint6) {\r\n            return nUint6 < 26 ? nUint6 + 65 : nUint6 < 52 ? nUint6 + 71 : nUint6 < 62 ? nUint6 - 4 : nUint6 === 62 ? 43 : nUint6 === 63 ? 47 : 65;\r\n        },\r\n        /**\r\n         * Array of bytes to base64 string decoding\r\n         * @method b64ToUint6\r\n         * @param {String} nChr\r\n         * @return {Number}\r\n         */\r\n        b64ToUint6: function (nChr) {\r\n            return nChr > 64 && nChr < 91 ? nChr - 65 : nChr > 96 && nChr < 123 ? nChr - 71 : nChr > 47 && nChr < 58 ? nChr + 4 : nChr === 43 ? 62 : nChr === 47 ? 63 : 0;\r\n\r\n        },\r\n        /**\r\n         * Decode base64 to array\r\n         * @method base64DecToArr\r\n         * @param {String} sBase64\r\n         * @param {Number} nBlocksSize\r\n         * @return {Number}\r\n         */\r\n        base64DecToArr: function (sBase64, nBlocksSize) {\r\n            var sB64Enc = sBase64.replace(/[^A-Za-z0-9\\+\\/]/g, \"\"), nInLen = sB64Enc.length, nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen);\r\n\r\n            for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {\r\n                nMod4 = nInIdx & 3;\r\n                nUint24 |= fr.ina.amalia.player.helpers.UtilitiesHelper.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;\r\n                if (nMod4 === 3 || nInLen - nInIdx === 1) {\r\n                    for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {\r\n                        taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;\r\n                    }\r\n                    nUint24 = 0;\r\n                }\r\n            }\r\n            return taBytes;\r\n        },\r\n        /**\r\n         * In charge to create unique id\r\n         * @method generateUUID\r\n         */\r\n        generateUUID: function (prefix) {\r\n            var d = new Date().getTime();\r\n            var p = prefix || '';\r\n            var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n                var r = (d + Math.random() * 16) % 16 | 0;\r\n                d = Math.floor(d / 16);\r\n                return (c === 'x' ? r : (r && 0x3 | 0x8)).toString(16);\r\n            });\r\n            return p + uuid;\r\n        },\r\n        /**\r\n         * Clear HTML escape\r\n         * @param {type} str\r\n         * @return {unresolved}\r\n         */\r\n        htmlEscape: function (str) {\r\n            return String(str)\r\n                .replace(/&/g, '&amp;')\r\n                .replace(/\"/g, '&quot;')\r\n                .replace(/'/g, '&#39;')\r\n                .replace(/</g, '&lt;')\r\n                .replace(/>/g, '&gt;');\r\n        }\r\n    },\r\n    {});"
  },
  {
    "path": "src/i18n/player-error-message-en.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\nfr.ina.amalia.player.PlayerErrorMessage.ACCESS_DENIED = 'Access denied.';\nfr.ina.amalia.player.PlayerErrorMessage.MEDIA_FILE_NOT_FOUND = 'Error Loading Media.';\nfr.ina.amalia.player.PlayerErrorMessage.EXCEPTION = 'An exception occurred.';\nfr.ina.amalia.player.PlayerErrorMessage.HTTP_ERROR = 'Http response at 400 or 500 level.';\nfr.ina.amalia.player.PlayerErrorMessage.ABORT = 'Your request has been interrupted.';\nfr.ina.amalia.player.PlayerErrorMessage.TIMEOUT = 'Timeout.';\nfr.ina.amalia.player.PlayerErrorMessage.ERROR_LOAD_PLUGIN = 'Error during plugin initialization.';\nfr.ina.amalia.player.PlayerErrorMessage.CUSTOM_ERROR = 'An error has occurred on your player.';\nfr.ina.amalia.player.PlayerErrorMessage.ERROR = 'Error has occurred please try again later.';\nfr.ina.amalia.player.PlayerErrorMessage.DEFAULT = 'Error has occurred please try again later.';\nfr.ina.amalia.player.PlayerErrorMessage.ERROR_HTML5_SUPPORT = 'Error loading player: HTML5 video not supported';\nfr.ina.amalia.player.PlayerErrorMessage.ERROR_MANIFEST_DASH = 'Error to load manifest dash';\n"
  },
  {
    "path": "src/i18n/player-message-en.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\nfr.ina.amalia.player.PlayerMessage.PLAYER_OPENSOURCE_LINK = 'Available as open source.';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_OVERLAY_CONTEXT_MENU_ENABLED_DISABLED_PLUGIN = 'Enable or disable plugin overlay.';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_ELEMENTS = \"items.\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_ZOOM_IN = \"Zoom in\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_ZOOM_OUT = \"Zoom out\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_CHANGE_DISPLAY = \"Switch lines height\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SLIDE_LEFT = \"Slide Left\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SLIDE_RIGHT = \"Slide Right\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SCROLL_UP = \"Scroll Up\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SCROLL_DOWN = \"Scroll Down\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SHOW_HIDE_TOOTIP = \"Enable/disable the help tooltip\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_EXPAND = \"Expand\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SORT = \"Sort\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_NAV_POINT_PREV = \"Go to previous marker\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_NAV_POINT_NEXT = \"Go to next marker\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_TIMEAXIS = \"Time axis\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_CLOSE = \"Close\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_LIST_EDITOR_LABEL_HEADER = \"Metadata list editor\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_LABEL_HEADER = \"Metadata block editor\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_LABEL_HEADER = \"Metadata items editor\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_ID = 'Id';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_LABAL = 'Label';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_TYPE = 'Type';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_DESCRIPTION = 'Description';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_REFERENCE = 'Reference';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_AUTHOR = 'Author';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_SHAPE = 'Shape';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_COLOR = 'Color';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA = 'You need to select metadata item type.';\nfr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_BIND = \"Bind/Unbind\";\nfr.ina.amalia.player.PlayerMessage.PLUGIN_D3JS_CHART_LABELS = ['Grouped', 'Stacked'];\n\n"
  },
  {
    "path": "src/ina-player.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * jQuery plugin for ina media player\r\n * @function mediaPlayer\r\n * @description jQuery media player plugin\r\n * @param Object settings settings\r\n * @module player\r\n */\r\n(function ($) {\r\n    $.fn.mediaPlayer = function (settings) {\r\n        return this.each(function () {\r\n            // The element to display media.\r\n            var containerElement = $(this);\r\n            var dataId = 'fr.ina.amalia.player';\r\n            // Return early if this element already has a plugin\r\n            // instance\r\n            if (containerElement.data(dataId)) {\r\n                return null;\r\n            }\r\n            settings = $.extend({\r\n                    player: 'default',\r\n                    plugins: {},\r\n                    src: null,\r\n                    framerate: 25,\r\n                    autoplay: true,\r\n                    controlBarClassName: \"fr.ina.amalia.player.plugins.CustomControlBarPlugin\",\r\n                    controlBar: {},\r\n                    tcOffset: 0,\r\n                    mediaType: '',\r\n                    thumbRootDirectory: '',\r\n                    duration: null,\r\n                    togglePlayPause: false,\r\n                    debug: false\r\n                },\r\n                settings || {});\r\n\r\n            var mediaFactory = new fr.ina.amalia.player.MediaFactory(this, settings);\r\n            // Store plugin object in this element's data\r\n            containerElement.data(dataId, mediaFactory);\r\n            containerElement.addClass('ajs');\r\n            containerElement.attr('style', 'width: 100%; height:100%;');\r\n            containerElement.parent().css('position', 'relative');\r\n        });\r\n    };\r\n})(jQuery);"
  },
  {
    "path": "src/log/.jshintrc",
    "content": "{\n  \"curly\": true,\n  \"eqeqeq\": true,\n  \"immed\": true,\n  \"latedef\": true,\n  \"newcap\": true,\n  \"noarg\": true,\n  \"sub\": true,\n  \"undef\": true,\n  \"unused\": true,\n  \"boss\": true,\n  \"eqnull\": true,\n  \"browser\": true,\n  \"predef\": [\n    \"jQuery\",\n    \"$\",\n    \"fr\",\n    \"console\"\n  ]\n}\n"
  },
  {
    "path": "src/log/log-handler.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * This class provides log utility functions.\r\n * @class LogHandler\r\n * @namespace fr.ina.amalia.player.log\r\n * @constructor\r\n * @param {Object} settings Defines the configuration of this class\r\n */\r\n$.Class(\"fr.ina.amalia.player.log.LogHandler\", {}, {\r\n    /**\r\n     * Defines configuration\r\n     * @property settings\r\n     * @type {Object}\r\n     * @default {}\r\n     */\r\n    settings: {},\r\n    init: function (settings) {\r\n        this.settings = $.extend({\r\n                enabled: false\r\n            },\r\n            settings || {});\r\n    },\r\n    /**\r\n     * Trace\r\n     * @method trace\r\n     * @param {String} className class name\r\n     * @param {Object} functionName function name\r\n     * @return {String} Returns log message\r\n     */\r\n    trace: function (className, functionName) {\r\n        var message = null;\r\n        if (this.settings.enabled && window.console && window.console.info) {\r\n            message = className + \":\" + functionName;\r\n            console.info(message);\r\n        }\r\n        return message;\r\n    },\r\n    /**\r\n     * Info\r\n     * @method info\r\n     * @param {String|Object} message log message\r\n     * @return {String|Object} log message\r\n     */\r\n    info: function (message) {\r\n        var msg = (message) ? message : null;\r\n        if (this.settings.enabled && window.console && window.console.debug) {\r\n            console.info(msg);\r\n        }\r\n        return msg;\r\n    },\r\n    /**\r\n     * Error\r\n     * @method error\r\n     * @param {String|Object} message log message\r\n     * @return {String|Object} log message\r\n     */\r\n    error: function (message) {\r\n        var msg = (message) ? message : null;\r\n        if (this.settings.enabled && window.console && window.console.error) {\r\n            console.error(msg);\r\n        }\r\n        return msg;\r\n    },\r\n    /**\r\n     * Warn\r\n     * @method warn\r\n     * @param {String|Object} message log message\r\n     * @return {String|Object} log message\r\n     */\r\n    warn: function (message) {\r\n        var msg = (message) ? message : null;\r\n        if (this.settings.enabled && window.console && window.console.warn) {\r\n            console.warn(msg);\r\n        }\r\n        return msg;\r\n    },\r\n    /**\r\n     * Warn\r\n     * @method warn\r\n     * @param {String|Object} message log message\r\n     * @return {String|Object} log message\r\n     */\r\n    debug: function (message) {\r\n        var msg = (message) ? message : null;\r\n        if (this.settings.enabled && window.console && window.console.debug) {\r\n            console.debug(msg);\r\n        }\r\n        return msg;\r\n    }\r\n\r\n});\r\n"
  },
  {
    "path": "src/player/base-player.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class\n * @class BasePlayer\n * @module player\n * @namespace fr.ina.amalia.player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaContainer\n */\n$.Class(\"fr.ina.amalia.player.BasePlayer\", {\n        mediaPlayerClassCss: \"player\",\n        mediaPlayerStyleCss: \"position: relative; width: inherit; height: inherit; background-color: black; \",\n        playButtonStyleCss: \"ajs-icon ajs-icon-controlbar-play\",\n        pauseButtonStyleCss: \"ajs-icon ajs-icon-controlbar-pause\",\n        loaderStyleCss: \"ajs-icon ajs-icon-cog\"\n\n    },\n    {\n        logger: null,\n        settings: {},\n        /**\n         * Media title\n         * @property title\n         * @type {Object}\n         * @default null\n         */\n        title: \"\",\n        /**\n         * Media poster\n         * @property poster\n         * @type {Object}\n         * @default null\n         */\n        poster: \"\",\n        /**\n         * Main container\n         * @property mediaContainer\n         * @type {Object}\n         * @default null\n         */\n        mediaContainer: null,\n        /**\n         * Player instance\n         * @property mediaPlayer\n         * @type {Object}\n         * @default null\n         */\n        mediaPlayer: null,\n        /**\n         * Plugin manager instance\n         * @property pluginManager\n         * @type {Object} fr.ina.amalia.player.plugins.PluginManager\n         * @default null\n         */\n        pluginManager: null,\n        /**\n         * Instance of local storage manager.\n         * @property localStorageManager\n         * @type {Object} fr.ina.amalia.player.LocalStorageManager\n         * @default null\n         */\n        localStorageManager: null,\n        /**\n         * Element to display loader\n         * @property loaderContainer\n         * @type {Object}\n         * @default null\n         */\n        loaderContainer: null,\n        /**\n         * Element to display play/pause button on screen\n         * @property controlLayerElement\n         * @type {Object}\n         * @default null\n         */\n        controlLayerElement: null,\n        /**\n         * Element to display error\n         * @property errorContainer\n         * @type {Object}\n         * @default null\n         */\n        errorContainer: null,\n        /**\n         * True play only range\n         * @property isRangePlayer\n         * @type {Boolean}\n         * @default false\n         */\n        isRangePlayer: false,\n        /**\n         * Tc for stop player in range mode\n         * @property rangePlayerTcout\n         * @type {Number}\n         * @default null\n         */\n        rangePlayerTcout: null,\n        /**\n         * Image capture id\n         * @property captureId\n         * @type {Number}\n         * @default null\n         */\n        captureId: null,\n        /**\n         * Image capture tc\n         * @property captureTc\n         * @type {Number}\n         * @default null\n         */\n        captureTc: null,\n        /**\n         * Image caputre ratio\n         * @property captureScale\n         * @type {Number}\n         * @default 100\n         */\n        captureScale: 1,\n        /**\n         * Zoom tcin\n         * @property zTcin\n         * @type {Number}\n         * @default null\n         */\n        zTcin: null,\n        /**\n         * Zoom tcout\n         * @property zTcout\n         * @type {Number}\n         * @default null\n         */\n        zTcout: null,\n        /**\n         * Tc offset\n         * @property tcOffset\n         * @type {Number}\n         * @default 0\n         */\n        tcOffset: 0,\n        /**\n         * Media type manager, if your media is not mp4 file.\n         * @property tcOffset\n         * @type {Number}\n         * @default 0\n         */\n        mediaTypeManager: null,\n        /**\n         * In charge to manage all metadata\n         * @property metadataManager\n         * @type {Object}\n         * @default null\n         */\n        metadataManager: null,\n        /**\n         * timeout representing the ID\n         * @property intervalRewind\n         * @type {Object}\n         * @default null\n         */\n        intervalRewind: null,\n        /**\n         * Shortcut manager\n         * @property shortcutManager\n         * @type {Object}\n         * @default null\n         */\n        shortcutManager: null,\n        /**\n         * playbackRate speed\n         * @property playbackRate\n         * @type {Number}\n         * @default null\n         */\n        playbackRateIdx: 7,\n        playbackRateList: [\n            -2,\n            -1.5,\n            -1,\n            -0.75,\n            -0.5,\n            -0.25,\n            -0.1,\n            1,\n            0.5,\n            0.75,\n            1.5,\n            2,\n            4,\n            6,\n            8\n        ],\n        /**\n         * True if ios devices\n         * @property isIOSDevices\n         * @type {Boolean}\n         * @default false\n         */\n        isIOSDevices: false,\n        /**\n         * media source url\n         * @property source\n         * @type {Boolean}\n         * @default false\n         */\n        mediaSourceUrl: \"\",\n        /**\n         * In Cast Mode\n         * @property inCastMode\n         * @type {Boolean}\n         * @default false\n         */\n        inCastMode: false,\n\n\n        /**\n         * Init player class\n         * @constructor\n         * @param {Object} settings\n         * @param {Object} mediaContainer\n         */\n        init: function (settings, mediaContainer) {\n            this.controlLayerElement = null;\n            this.mediaSourceUrl = \"\";\n            this.inCastMode = false;\n            this.settings = $.extend({\n                autoplay: false,\n                poster: \"\",\n                src: \"\",\n                title: \"\",\n                defaultVolume: 75,\n                crossorigin: '',\n                shortcuts: {},\n                defaultPlaybackRateIdx: 7,\n                duration: null\n            }, settings || {});\n            if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.mediaContainer = mediaContainer;\n            this.setStyleBreakpoint();\n            this.tcOffset = parseInt(this.settings.tcOffset);\n            this.createLoaderContainer();\n            if (this.settings.togglePlayPause === true) {\n                this.createControlLayerElement();\n            }\n            this.createErrorContainer();\n            this.localStorageManager = new fr.ina.amalia.player.LocalStorageManager({});\n            this.shortcutManager = new fr.ina.amalia.player.ShortcutsManager(this.settings.shortcuts, this);\n            this.playbackRateIdx = this.settings.defaultPlaybackRateIdx;\n            var IsiPhone = navigator.userAgent.indexOf(\"iPhone\") !== -1;\n            var IsiPod = navigator.userAgent.indexOf(\"iPod\") !== -1;\n            var IsiPad = navigator.userAgent.indexOf(\"iPad\") !== -1;\n            this.isIOSDevices = IsiPhone || IsiPad || IsiPod;\n            this.setTitle(this.settings.title);\n            this.setPoster(this.settings.poster);\n            this.initialize();\n        },\n        /**\n         * initialize plugin manager\n         * @method initializePlugins\n         */\n        initializePlugins: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initializePlugins\");\n            }\n            this.pluginManager = new fr.ina.amalia.player.plugins.PluginManager(this.settings, this, this.mediaContainer);\n        },\n        /**\n         * initialize plugin manager\n         * @method initializeMetadataManager\n         */\n        initializeMetadataManager: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initializeMetadataManager\");\n            }\n            this.metadataManager = new fr.ina.amalia.player.MetadataManager(this.settings, this, this.mediaContainer);\n        },\n        /**\n         * In charge to create loader container\n         * @method createLoaderContainer\n         */\n        createLoaderContainer: function () {\n            this.loaderContainer = $('<div>', {\n                'class': 'ajs-loader'\n            });\n            var loader = $('<i>', {\n                class: this.Class.loaderStyleCss\n            });\n            this.loaderContainer.append(loader);\n            this.mediaContainer.append(this.loaderContainer);\n            this.showLoader();\n        },\n        /**\n         * In charge to create control layer element\n         * @method createControlLayerElement\n         */\n        createControlLayerElement: function () {\n            this.controlLayerElement = $('<div>', {\n                'class': 'ajs-control-layer'\n            });\n            this.controlLayerElement.append($('<i>'));\n            this.mediaContainer.append(this.controlLayerElement);\n        },\n        /**\n         * In charge to create error container\n         * @method createErrorContainer\n         */\n        createErrorContainer: function () {\n            this.errorContainer = $('<div>', {\n                'class': 'ajs-error'\n            });\n            this.errorContainer.hide();\n            this.mediaContainer.append(this.errorContainer);\n        },\n        /**\n         * In charge to create error container\n         * @method createErrorContainer\n         */\n        getContainer: function () {\n            return this.mediaContainer;\n        },\n        /**\n         * Method in charge to initialize player : - Create containers - Load\n         * plugins\n         * @method initialize\n         */\n        initialize: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n            // plugins\n            this.initializePlugins();\n            this.initializeMetadataManager();\n            this.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.INIT, [\n                this\n            ]);\n            this.getContainer().on(fr.ina.amalia.player.PlayerEventType.ERROR, {\n                self: this\n            }, this.onError);\n        },\n        /**\n         * In charge to load media\n         * @method load\n         */\n        load: function () {\n            this.mediaPlayer.get(0).load();\n        },\n        /**\n         * In charge to show loader.\n         * @method showControlLayerElement\n         */\n        showControlLayerElement: function (action) {\n            if (this.controlLayerElement !== null) {\n                var btn = action === \"play\" ? this.Class.playButtonStyleCss : this.Class.pauseButtonStyleCss;\n                this.controlLayerElement.find('i').attr('class', btn);\n                var self = this;\n                this.controlLayerElement.fadeIn(\"speed\").addClass(\"scale-up\").delay(200).queue(function () {\n                    self.controlLayerElement.removeClass(\"scale-up\").fadeOut(\"speed\").dequeue();\n                });\n            }\n        },\n        /**\n         * In charge to show loader.\n         * @method showLoader\n         */\n        showLoader: function () {\n            this.loaderContainer.show();\n        },\n        /**\n         * In charge to hide loader.\n         * @method hideLoader\n         */\n        hideLoader: function () {\n            this.loaderContainer.hide();\n        },\n        /**\n         * return the title of the media\n         * @param {String} title\n         * @method getTitle\n         * @abstract\n         */\n        getTitle: function () {\n            return this.title;\n        },\n        /**\n         * Set the title of the media\n         * @param {String} src\n         * @method setSrc\n         * @abstract\n         */\n        setTitle: function (title) {\n            this.title = title;\n        },\n        /**\n         * return the cast state\n         * @param {String} inCastMode\n         * @method getCastMode\n         * @abstract\n         */\n        getCastMode: function () {\n            return this.inCastMode;\n        },\n        /**\n         * Set the cast state\n         * @param {String} src\n         * @method setCastMode\n         * @abstract\n         */\n        setCastMode: function (state) {\n            this.inCastMode = state;\n        },\n        /**\n         * return the title of the media\n         * @param {String} title\n         * @method getTitle\n         * @abstract\n         */\n        getPoster: function () {\n            return this.poster;\n        },\n        /**\n         * Set the title of the media\n         * @param {String} src\n         * @method setSrc\n         * @abstract\n         */\n        setPoster: function (poster) {\n            this.poster = poster;\n        },\n\n\n        /**\n         * In charge to set source\n         * @param {String} src\n         * @param {Boolean} autoplay\n         * @method setSrc\n         * @abstract\n         */\n        setSrc: function (src, autoplay) {\n            this.mediaSourceUrl = src;\n            if (autoplay === true) {\n                this.play();\n            }\n        },\n        /**\n         * In charge to set source\n         * @param {String} src\n         * @param {Boolean} autoplay\n         * @method getSrc\n         * @abstract\n         */\n        getSrc: function () {\n            return this.mediaSourceUrl;\n        },\n        /**\n         * Return source content type\n         * @method getContentType\n         * @abstract\n         */\n        getContentType: function () {\n            return '';\n        },\n        /**\n         * In charge to set player events\n         * @method initEvents\n         */\n        initEvents: function () {\n            // call function 200 ms after resize is complete.\n            $(window).on('debouncedresize', {\n                self: this\n            }, this.onWindowResize);\n            if (this.logger !== null) {\n                this.logger.info(\"initEvents\");\n            }\n        },\n        /**\n         * In charge to set style brack point\n         * @method setStyleBreakpoint\n         */\n        setStyleBreakpoint: function () {\n            var style = 'lg';\n            var playerWidth = this.mediaContainer.width();\n            // player width\n            if (playerWidth <= 320) {\n                style = 'xxs';\n            }\n            else if (playerWidth > 320 && playerWidth <= 480) {\n                style = 'xs';\n            }\n            else if (playerWidth > 480 && playerWidth <= 768) {\n                style = 'sm';\n            }\n            else if (playerWidth > 768 && playerWidth <= 992) {\n                style = 'md';\n            }\n\n            //Style\n            if (this.mediaContainer.hasClass(style) === false) {\n                this.mediaContainer.removeClass('xxs').removeClass('xs').removeClass('sm').removeClass('md').removeClass('lg').addClass(style);\n            }\n            return style;\n        },\n        /**\n         * In charge to play media\n         * @method play\n         */\n        play: function () {\n            this.mediaContainer.addClass('playing-mode').removeClass('paused-mode');\n            if (typeof this.settings.callbacks.onPlay !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(this.settings.callbacks.onPlay + '()');\n                }\n                catch (e) {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n        },\n        /**\n         * In charge to pause media\n         * @method pause\n         */\n        pause: function () {\n            this.mediaContainer.removeClass('playing-mode').addClass('paused-mode');\n            this.isRangePlayer = false;\n            this.rangePlayerTcout = null;\n            if (typeof this.settings.callbacks.onPause !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(this.settings.callbacks.onPause + '()');\n                }\n                catch (e) {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n        },\n        /**\n         * In charge to stop media\n         * @method stop\n         */\n        stop: function () {\n            if (this.settings.callbacks.hasOwnProperty(\"onStop\") && typeof this.settings.callbacks.onStop !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(this.settings.callbacks.onStop + '()');\n                }\n                catch (e) {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n        },\n        /**\n         * In charge toggle Play\n         * @method stop\n         */\n        togglePlayPause: function () {\n            if (this.isPaused()) {\n                this.play();\n                this.showControlLayerElement('play');\n            }\n            else {\n                this.pause();\n                this.showControlLayerElement('pause');\n            }\n        },\n        /**\n         * In charge to seek\n         * @method seek\n         * @param {Number} time\n         * @return {Number} current time\n         */\n        seek: function (time) {\n            return this.setCurrentTime(time);\n        },\n        /**\n         * Set current time to the beginning of the file\n         * @method seekToBegin\n         * @param {Number} time\n         * @return {Number} current time\n         */\n        seekToBegin: function () {\n            return this.setCurrentTime(0);\n        },\n        /**\n         * Set current time to the end of the file\n         * @method seekToEnd\n         * @param {Number} time\n         * @return {Number} current time\n         */\n        seekToEnd: function () {\n            return this.setCurrentTime(this.getDuration());\n        },\n        /**\n         * In charge to set mute state\n         * @method mute\n         * @event fr.ina.amalia.player.PlayerEventType.MUTE\n         * @return {Number}\n         */\n        mute: function () {\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.MUTE);\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.VOLUME_CHANGE, {\n                volume: 0\n            });\n        },\n        /**\n         * In charge to set unmute state\n         * @method unmute\n         * @event fr.ina.amalia.player.PlayerEventType.UN_MUTE\n         */\n        unmute: function () {\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.UN_MUTE);\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.VOLUME_CHANGE, {\n                volume: 100\n            });\n        },\n        /**\n         * Return true if is mute\n         * @method isMute\n         * @event fr.ina.amalia.player.PlayerEventType.UN_MUTE\n         */\n        isMute: function () {\n            return this.getVolume() === 0;\n        },\n        /**\n         * Return player instance\n         * @method muteUnmute\n         * @return {Object}\n         */\n        muteUnmute: function () {\n            if (this.isMute()) {\n                this.unmute();\n            }\n            else {\n                this.mute();\n            }\n        },\n        /**\n         * Return tc offset\n         * @returns {Number}\n         */\n        getTcOffset: function () {\n            return this.tcOffset;\n        },\n        /**\n         * Return player instance\n         * @method getMediaPlayer\n         * @return {Object}\n         */\n        getMediaPlayer: function () {\n            return this.mediaPlayer;\n        },\n        /**\n         * Return media duration with tc offset\n         * @method getDuration\n         * @return {Number}\n         */\n        getDuration: function (duration) {\n            return typeof duration === \"number\" && isFinite(duration) ? duration : 0;\n        },\n        /**\n         * Returns the current playback volume percentage, as a number from 0 to\n         * 100.\n         * @method getVolume\n         * @return {Number}\n         */\n        getVolume: function () {\n            throw new Error(\"Get volume is abstract method.\");\n        },\n        /**\n         * In charge to play segment\n         * @param {Number} tcin\n         * @param {Number} tcout\n         * @param {Boolean} autoplay true for autolay\n         */\n        rangePlay: function (tcin, tcout, autoplay) {\n            autoplay = (typeof autoplay === \"undefined\") ? false : autoplay;\n            this.isRangePlayer = true;\n            this.rangePlayerTcout = tcout + this.tcOffset;\n            this.setCurrentTime(tcin);\n            if (autoplay === true) {\n                this.play();\n            }\n        },\n        /**\n         * Sets the player's audio volume percentage, as a number between 0 and 100.\n         * @method setVolume\n         * @param {Number} value\n         */\n        setVolume: function (value) {\n            if (value >= 0 && value <= 100) {\n                this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.VOLUME_CHANGE, {\n                    volume: value\n                });\n                this.localStorageManager.setItem('volume', value);\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"setVolume\" + value);\n                }\n                return true;\n            }\n            return false;\n        },\n        /**\n         * Return current position in seconds\n         * @method getCurrentTime\n         * @return {Number}\n         */\n        getCurrentTime: function (currentTime) {\n            if (this.settings.duration === null) {\n                return currentTime + this.tcOffset;\n            } else {\n                return this.lastSeekTime + currentTime + this.tcOffset;\n            }\n        },\n        /**\n         * Set seek position in seconds\n         * @method setCurrentTime\n         * @param {Object} currentTime\n         * @event fr.ina.amalia.player.PlayerEventType.SEEK\n         * @return return current time without tc offset.\n         */\n        setCurrentTime: function (value) {\n            var currentTime = isNaN(value) ? 0 : value;\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.SEEK, {\n                currentTime: currentTime\n            });\n        },\n\n        /**\n         * Return playback rate\n         * @returns the current playback speed of the audio/video.\n         */\n        getPlaybackrate: function () {\n            throw new Error(\"Get volume is abstract method.\");\n        },\n        /**\n         * Set playback rate\n         * @param {Objecy} speed the current playback speed of the audio/video.\n         * @returns the current playback speed of the audio/video.\n         */\n        setPlaybackrate: function (speed) {\n            throw new Error(\"Get volume is abstract method. Speed: \" + speed);\n        },\n        upPlaybackrate: function () {\n            this.playbackRateIdx = Math.min(this.playbackRateList.length, this.playbackRateIdx + 1);\n            this.setPlaybackrate(parseInt(this.playbackRateList[this.playbackRateIdx]));\n        },\n        downPlaybackrate: function () {\n            this.playbackRateIdx = Math.max(0, this.playbackRateIdx - 1);\n            this.setPlaybackrate(parseInt(this.playbackRateList[this.playbackRateIdx]));\n        },\n        /**\n         * Set Error\n         * @method setErrorCode\n         * @param {Object} errorCode\n         */\n        setErrorCode: function (errorCode) {\n            if (typeof this.errorContainer !== \"undefined\") {\n                var messageContainer = $('<p>', {\n                    text: fr.ina.amalia.player.PlayerErrorCode.getMessage(errorCode)\n                });\n                this.errorContainer.html(messageContainer);\n                this.errorContainer.show();\n            }\n        },\n        /**\n         * Returns the current fullscreen state\n         * @method getFullscreenState\n         * @return {Boolean} description\n         */\n        getFullscreenState: function () {\n            throw new Error(\"Get fullscreen is abstract method.\");\n        },\n\n        /**\n         * Return true if media is paused\n         * @method isPaused\n         * @return {Boolean}\n         */\n        isPaused: function () {\n            throw new Error(\"Is paused is abstract method.\");\n        },\n        /**\n         * In charge to toggle full-screen state\n         * @see HTML5 Fullscreen API\n         * @method toggleFullScreen\n         * @return {Boolean} true\n         */\n        toggleFullScreen: function (inFullScreen) {\n            var container = (this.isIOSDevices) ? this.mediaPlayer.get(0) : this.mediaContainer.get(0);\n            var iFS = fr.ina.amalia.player.helpers.HTML5Helper.toggleFullScreen(container, this.isIOSDevices);\n            if (typeof this.settings.callbacks.onFullscreen !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(this.settings.callbacks.onFullscreen + '(inFullScreen)');\n                }\n                catch (e) {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onClickFullscreenButton :\" + inFullScreen);\n            }\n            return iFS;\n        },\n        /**\n         * Returns the current fullscreen state\n         * @method getFullscreenState\n         * @return {Boolean} description\n         */\n        getContentTupe: function () {\n            throw new Error(\"Get media content type is abstract method.\");\n        },\n        /**\n         * Return current image\n         * @param {Nomber} scale max 1=> 100%\n         * @method getCurrentImage\n         * @event fr.ina.amalia.player.PlayerEventType.IMAGE_CAPTURE\n         */\n        getCurrentImage: function (scale) {\n            throw new Error(\"Get currentTime is abstract method. Scale: \" + scale);\n        },\n        /**\n         * Return current image for specified time code.\n         * @param {String} id\n         * @param {Number} tc\n         * @param {Number} scale max 1=> 100%\n         * @method getTcImage\n         * @event fr.ina.amalia.player.PlayerEventType.IMAGE_CAPTURE\n         */\n        getTcImage: function (id, tc, scale) {\n            this.setCurrentTime(tc);\n            // Need to call player for make capture\n            this.play();\n            this.captureId = id;\n            this.captureTc = tc + this.tcOffset;\n            this.captureScale = (typeof scale !== 'undefined') ? Math.min(1, parseFloat(scale)) : 1;\n        },\n        /**\n         * In charge to move next frame\n         * @method moveNextFrame\n         * @param {Object} event\n         */\n        moveNextFrame: function () {\n            this.pause();\n            this.setCurrentTime(Math.min(this.getDuration() + this.tcOffset, this.getCurrentTime() + (1 / this.settings.framerate)));\n        },\n        /**\n         * In charge to move prev frame\n         * @method movePrevFrame\n         * @param {Object} event\n         */\n        movePrevFrame: function () {\n            this.pause();\n            this.setCurrentTime(Math.max(0, this.getCurrentTime() - (1 / this.settings.framerate)));\n        },\n\n        /**\n         * In charge to add menu context item\n         * @param {String} title\n         * @param {String} link\n         * @param {String} className\n         */\n        addMenuItemWithLink: function (title, link, className) {\n            if (typeof this.pluginManager === \"object\" && typeof this.pluginManager.getContextMenuPlugin === \"function\") {\n                var contextMenuPlugin = this.pluginManager.getContextMenuPlugin();\n                if (typeof contextMenuPlugin === \"object\" && typeof contextMenuPlugin.addItemWithLink === \"function\") {\n                    return contextMenuPlugin.addItemWithLink(title, link, className);\n                }\n            }\n            return false;\n        },\n\n        /** Metadata **/\n        /**\n         * Return instance of metadata manager\n         */\n        getMetadataManager: function () {\n            return this.metadataManager;\n        },\n        /**\n         * Return all block of metadata\n         */\n        getBlocksMetadata: function () {\n            return this.metadataManager.getBlocksMetadata();\n        },\n        /**\n         * Return the block of metadata by id\n         */\n        getBlockMetadata: function (id) {\n            return this.metadataManager.getBlockMetadata(id);\n        },\n        /**\n         * Update the block metadata\n         */\n        updateBlockMetadata: function (id, data, action) {\n            this.metadataManager.updateBlockMetadata(id, data, action);\n        },\n        /**\n         * Remove the block metadata with id metadata\n         */\n        removeBlockMetadata: function (id) {\n            return this.metadataManager.removeBlockMetadata(id);\n        },\n        /**\n         * In charge to add metadata, it is called by metadata parser\n         * @method addAllMetadata\n         * @param {Object} data\n         * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n         */\n        addAllMetadata: function (parsedData) {\n            this.metadataManager.addAllMetadata(parsedData);\n        },\n        /**\n         * In charge to return metadata by id\n         * @method addMetadataById\n         * @param {Object} data\n         * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n         */\n        addMetadataById: function (id, parsedData) {\n            this.metadataManager.addMetadataById(id, parsedData);\n        },\n        /**\n         * In charge to replace metadata by id\n         * @method replaceAllMetadataById\n         * @param {Object} data\n         * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n         */\n        replaceAllMetadataById: function (id, parsedData) {\n            this.metadataManager.replaceAllMetadataById(id, parsedData);\n        },\n        /**\n         * In charge to delete metadata by id\n         * @method deleteAllMetadataById\n         * @param {Object} data\n         * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n         */\n        deleteAllMetadataById: function (id) {\n            this.metadataManager.deleteAllMetadataById(id);\n        },\n        /**\n         * Return metadata by id\n         * @method getMetadataById\n         * @param {String} id\n         * @return {Array}\n         */\n        getMetadataById: function (id) {\n            return this.metadataManager.getMetadataById(id);\n        },\n        /**\n         * Set metadata by id\n         * @method setMetadataById\n         * @param {String} id\n         * @param {Object} data\n         * @return {Array}\n         */\n        setMetadataById: function (id, data) {\n            return this.metadataManager.setMetadataById(id, data);\n        },\n        /**\n         * In charge to remove metadata\n         * @method removeMetadataById\n         */\n        removeMetadataById: function (id) {\n            return this.metadataManager.removeMetadataById(id);\n        },\n        /**\n         * Return a metadata by specified time code.\n         * @method getMetadataWithRange\n         * @param id\n         * @param tcin\n         * @param tcout\n         * @return {Array}\n         */\n        getMetadataWithRange: function (id, tcin, tcout) {\n            return this.metadataManager.getMetadataWithRange(id, tcin, tcout);\n        },\n        /**\n         * Return selected component id\n         * @method getSelectedMetadataId\n         */\n        getSelectedMetadataId: function () {\n            return this.metadataManager.getSelectedMetadataId();\n        },\n        /**\n         * Set selected component id\n         * @method setSelectedMetadataId\n         */\n        setSelectedMetadataId: function (metadataId) {\n            this.metadataManager.setSelectedMetadataId(metadataId);\n        },\n        /**\n         * In charge to add metadata item\n         * @method addMetadataItem\n         */\n        addMetadataItem: function (metadataId, data) {\n            return this.metadataManager.addMetadataItem(metadataId, data);\n        },\n        /**\n         * Return selected items\n         */\n        getSelectedItems: function () {\n            return this.metadataManager.getSelectedItems();\n        },\n        /**\n         * Add selected item\n         */\n        addSelectedItem: function (item) {\n            this.metadataManager.addSelectedItem(item);\n        },\n        /**\n         * Remove all selected items\n         */\n        removeAllSelectedItems: function () {\n            this.metadataManager.removeAllSelectedItems();\n        },\n        /**\n         * Return tcin with tc offset\n         * @returns {Number}\n         */\n        getTcin: function () {\n            return this.getTcOffset();\n        },\n        /**\n         * Return Tcout with tcoffset and media duration\n         * @returns {Number}\n         */\n        getTcout: function () {\n            return this.getTcOffset() + this.getDuration();\n        },\n        /**\n         * In charge to set zoom tc\n         * @param {Number} zTcin\n         * @param {Number} zTcout\n         * @param {String} eventTag\n         */\n        setZoomTc: function (zTcin, zTcout, eventTag) {\n            eventTag = (typeof eventTag !== 'undefined') ? eventTag : '';\n            if (Math.ceil(this.zTcin) !== Math.ceil(zTcin) || Math.ceil(this.zTcout) !== Math.ceil(zTcout)) {\n                this.zTcin = Math.max(0, parseFloat(zTcin));\n                this.zTcout = parseFloat(zTcout);\n                if (this.logger !== null) {\n                    this.logger.info(\"SetZoomTc: // Tcin: \" + this.zTcin + \" // Tcout:\" + this.zTcout + \" // Event Tag Name:\" + eventTag);\n                }\n                this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.ZOOM_RANGE_CHANGE, {\n                    zTcin: parseFloat(zTcin),\n                    zTcout: parseFloat(zTcout),\n                    eventTag: eventTag\n                });\n            }\n        },\n        /**\n         * In Charge to remove instance\n         */\n        destroy: function () {\n            //Fix clear socket\n            this.setSrc(\"\", false);\n            if (typeof this.mediaContainer.data(\"fr.ina.amalia.player\") !== \"undefined\") {\n                this.mediaContainer.data(\"fr.ina.amalia.player\", null);\n            }\n            //clear html content\n            this.mediaContainer.get(0).innerHTML = \"\";\n            this.mediaContainer.remove();\n        },\n        /**\n         * Fired on player has errors.\n         * @method onError\n         * @param {Object} event\n         * @param {Object} data\n         * @event fr.ina.amalia.player.PlayerEventType.ERROR\n         */\n        onError: function (event, data) {\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onError\");\n            }\n            var errorCode = (typeof data.errorCode === \"undefined\") ? '' : data.errorCode;\n            event.data.self.setErrorCode(errorCode);\n            if (typeof event.data.self.settings.callbacks.onError !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(event.data.self.settings.callbacks.onError + '(errorCode)');\n                }\n                catch (e) {\n                    if (event.data.self.logger !== null) {\n                        event.data.self.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n        },\n        /**\n         * Fired on windows resize\n         * @method onWindowResize\n         * @param {Object} event\n         */\n        onWindowResize: function (event) {\n            event.data.self.setStyleBreakpoint();\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onWindowResize\");\n            }\n        }\n\n    });\n"
  },
  {
    "path": "src/player/constants/player-error-code.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Collected of player error code\n * @class PlayerErrorCode\n * @namespace fr.ina.amalia.player\n * @module player\n * @submodule player-constants\n */\n$.Class(\"fr.ina.amalia.player.PlayerErrorCode\", {\n        /**\n         * @constant\n         * @property ACCESS_DENIED\n         * @type {Number}\n         */\n        ACCESS_DENIED: 1001,\n        /**\n         * @constant\n         * @property MEDIA_FILE_NOT_FOUND\n         * @type {Number}\n         */\n        MEDIA_FILE_NOT_FOUND: 2001,\n        /**\n         * @constant\n         * @property CUSTOM_ERROR\n         * @type {Number}\n         */\n        CUSTOM_ERROR: 3001,\n        /**\n         * @constant\n         * @property EXCEPTION\n         * @type {Number}\n         */\n        EXCEPTION: 4001,\n        /**\n         * @constant\n         * @property HTTP_ERROR\n         * @type {Number}\n         */\n        HTTP_ERROR: 5001,\n        /**\n         * @constant\n         * @property ABORT\n         * @type {Number}\n         */\n        ABORT: 6001,\n        /**\n         * @constant\n         * @property TIMEOUT\n         * @type {Number}\n         */\n        TIMEOUT: 7008,\n        /**\n         * @constant\n         * @property ERROR\n         * @type {Number}\n         */\n        ERROR: 8008,\n        /**\n         * @constant\n         * @property ERROR_HTML5_SUPPORT\n         * @type {Number}\n         */\n        ERROR_HTML5_SUPPORT: 8000,\n        /**\n         * @constant\n         * @property ERROR_LOAD_PLUGIN\n         * @type {Number}\n         */\n        ERROR_LOAD_PLUGIN: 9001,\n        /**\n         * @constant\n         * @property ERROR_MANIFEST_DASH\n         * @type {Number}\n         */\n        ERROR_MANIFEST_DASH: 9002,\n\n        /**\n         * Return message by code error\n         * @param {Number} errorCode\n         * @returns {String} Return message\n         */\n        getMessage: function (errorCode) {\n            var CodeClass = fr.ina.amalia.player.PlayerErrorCode;\n            var MessageClass = fr.ina.amalia.player.PlayerErrorMessage;\n            switch (errorCode) {\n                case CodeClass.ACCESS_DENIED :\n                    return MessageClass.ACCESS_DENIED;\n                case CodeClass.MEDIA_FILE_NOT_FOUND :\n                    return MessageClass.MEDIA_FILE_NOT_FOUND;\n                case CodeClass.EXCEPTION :\n                    return MessageClass.EXCEPTION;\n                case CodeClass.HTTP_ERROR :\n                    return MessageClass.HTTP_ERROR;\n                case CodeClass.ABORT :\n                    return MessageClass.ABORT;\n                case CodeClass.TIMEOUT :\n                    return MessageClass.TIMEOUT;\n                case CodeClass.ERROR_LOAD_PLUGIN :\n                    return MessageClass.ERROR_LOAD_PLUGIN;\n                case CodeClass.CUSTOM_ERROR :\n                    return MessageClass.CUSTOM_ERROR;\n                case CodeClass.ERROR :\n                    return MessageClass.ERROR;\n                case CodeClass.ERROR_HTML5_SUPPORT :\n                    return MessageClass.ERROR_HTML5_SUPPORT;\n                case CodeClass.ERROR_MANIFEST_DASH:\n                    return MessageClass.ERROR_MANIFEST_DASH;\n                default :\n                    return MessageClass.DEFAULT;\n            }\n        }\n    },\n    {});\n\n/**\n * Constant class for error message\n * @class PlayerErrorMessage:\n * @namespace fr.ina.amalia.player\n * @module player\n * @submodule player-constants\n */\n$.Class(\"fr.ina.amalia.player.PlayerErrorMessage\", {\n    ACCESS_DENIED: 'Accès refusé.',\n    MEDIA_FILE_NOT_FOUND: 'Erreur lors du chargement du média.',\n    EXCEPTION: 'An exception occurred.',\n    HTTP_ERROR: 'Http response at 400 or 500 level.',\n    ABORT: 'Votre requête a été interrompue.',\n    TIMEOUT: 'Demande dépassé.',\n    ERROR_LOAD_PLUGIN: 'Erreur inattendue lors de l\\'initialisation du plug-in.',\n    CUSTOM_ERROR: 'Une erreur s\\'est produite sur votre lecteur.',\n    ERROR: 'Une erreur s\\'est produite sur votre lecteur.',\n    DEFAULT: 'Une erreur s\\'est produite sur votre lecteur.',\n    ERROR_HTML5_SUPPORT: 'Une erreur s\\'est produite sur votre lecteur: Votre navigateur ne gère pas les balises HTML5.',\n    ERROR_MANIFEST_DASH: 'Erreur lors du chargement du manifest dash'\n}, {});\n"
  },
  {
    "path": "src/player/constants/player-event-type.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Player event type\n * @class PlayerEventType\n * @namespace fr.ina.amalia.player\n * @module player\n * @submodule player-constants\n */\n$.Class(\"fr.ina.amalia.player.PlayerEventType\", {\n        /**\n         * Fired when the player has initialized.\n         * @constant\n         * @property INIT\n         * @type {String}\n         */\n        INIT: \"fr.ina.amalia.player.PlayerEventType.INIT\",\n        /**\n         * Fired when the player has started with duration.\n         * @constant\n         * @property STARTED\n         * @type {String}\n         */\n        STARTED: \"fr.ina.amalia.player.PlayerEventType.STARTED\",\n        /**\n         * Fires when playing event.\n         * @constant\n         * @property PLAYING\n         * @type {String}\n         */\n        CAST_PLAYING: \"fr.ina.amalia.player.PlayerEventType.CAST_PLAYING\",\n        /**\n         * Fires the paused/resumed\n         * @constant\n         * @property PAUSED\n         * @type {String}\n         */\n        CAST_PAUSED: \"fr.ina.amalia.player.PlayerEventType.CAST_PAUSED\",\n        /**\n         * Fires when playing event.\n         * @constant\n         * @property PLAYING\n         * @type {String}\n         */\n        PLAYING: \"fr.ina.amalia.player.PlayerEventType.PLAYING\",\n        /**\n         * Fires the paused/resumed\n         * @constant\n         * @property PAUSED\n         * @type {String}\n         */\n        PAUSED: \"fr.ina.amalia.player.PlayerEventType.PAUSED\",\n        /**\n         * Ended event should fire when a video is completely.\n         * @constant\n         * @property ENDED\n         * @type {String}\n         */\n        ENDED: \"fr.ina.amalia.player.PlayerEventType.ENDED\",\n        /**\n         * This fires when the volume level is equal to 0.\n         * @constant\n         * @property MUTE\n         * @type {String}\n         */\n        MUTE: \"fr.ina.amalia.player.PlayerEventType.MUTE\",\n        /**\n         * This fires when the volume level is equal to 0.\n         * @constant\n         * @property UN_MUTE\n         * @type {String}\n         */\n        UN_MUTE: \"fr.ina.amalia.player.PlayerEventType.UN_MUTE\",\n        /**\n         * This fires when the volume level is changed.\n         * @constant\n         * @property UN_MUTE\n         * @type {String}\n         */\n        VOLUME_CHANGE: \"fr.ina.amalia.player.PlayerEventType.VOLUME_CHANGE\",\n        /**\n         * This fires when the time change with attributes : 'obj' : instance of\n         * player 'currentTime' : player currentTime 'duration' : media duration\n         * 'percent'\n         * @constant\n         * @property TIME_CHANGE\n         * @type {String}\n         */\n        TIME_CHANGE: \"fr.ina.amalia.player.PlayerEventType.TIME_CHANGE\",\n        /**\n         * This fires when full-screen mode change\n         * @constant\n         * @property FULLSCREEN_CHANGE\n         * @type {String}\n         */\n        FULLSCREEN_CHANGE: \"fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGES\",\n        /**\n         * Fired when the user seeks.\n         * @constant\n         * @property SEEK\n         * @type {String}\n         */\n        SEEK: \"fr.ina.amalia.player.PlayerEventType.SEEK\",\n        /**\n         * Fired when a media error has occurred.\n         * @constant\n         * @property ERROR\n         * @type {String}\n         */\n        ERROR: \"fr.ina.amalia.player.PlayerEventType.ERROR\",\n        /**\n         * Fired when the player has initialized.\n         * @constant\n         * @property PLUGIN_READY\n         * @type {String}\n         * @deprecated\n         */\n        PLUGIN_READY: \"fr.ina.amalia.player.PlayerEventType.PLUGIN_READY\",\n        /**\n         * Fired when a plugin error has occurred.\n         * @constant\n         * @property PLUGIN_ERROR\n         * @type {String}\n         */\n        PLUGIN_ERROR: \"fr.ina.amalia.player.PlayerEventType.PLUGIN_ERROR\",\n        /**\n         * Fired when data change\n         * @constant\n         * @property PLUGIN_ERROR\n         * @type {String}\n         */\n        DATA_CHANGE: \"fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\",\n        /**\n         * Fired at the beginning of data change\n         * @constant\n         * @property BEGIN_DATA_CHANGE\n         * @type {String}\n         */\n        BEGIN_DATA_CHANGE: \"fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE\",\n        /**\n         * Fired end of data change\n         * @constant\n         * @property END_DATA_CHANGE\n         * @type {String}\n         */\n        END_DATA_CHANGE: \"fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE\",\n        /**\n         * Fired when image capture\n         * @constant\n         * @property IMAGE_CAPTURE\n         * @type {String}\n         */\n        IMAGE_CAPTURE: \"fr.ina.amalia.player.PlayerEventType.IMAGE_CAPTURE\",\n        /**\n         * Fired when zoom range change\n         * @constant\n         * @property ZOOM_RANGE_CHANGE\n         * @type {String}\n         */\n        ZOOM_RANGE_CHANGE: \"fr.ina.amalia.player.PlayerEventType.ZOOM_RANGE_CHANGE\",\n        /**\n         * Fired when selected metadata has changed\n         * @constant\n         * @property SELECTED_METADATA_CHANGE\n         * @type {String}\n         */\n        SELECTED_METADATA_CHANGE: \"fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE\",\n        /**\n         * Fired when selected item has changed\n         * @constant\n         * @property SELECTED_ITEMS_CHANGE\n         * @type {String}\n         */\n        SELECTED_ITEMS_CHANGE: \"fr.ina.amalia.player.PlayerEventType.SELECTED_ITEMS_CHANGE\",\n        /**\n         * Fired when bind metadata\n         * @constant\n         * @property BIND_METADATA\n         * @type {String}\n         */\n        BIND_METADATA: \"fr.ina.amalia.player.PlayerEventType.BIND_METADATA\",\n        /**\n         * Fired when unbind metadata\n         * @constant\n         * @property UNBIND_METADATA\n         * @type {String}\n         */\n        UNBIND_METADATA: \"fr.ina.amalia.player.PlayerEventType.UNBIND_METADATA\",\n        /**\n         * Fired when unbind metadata\n         * @constant\n         * @property PLAYBACK_RATE_CHANGE\n         * @type {String}\n         */\n        PLAYBACK_RATE_CHANGE: \"fr.ina.amalia.player.PlayerEventType.PLAYBACK_RATE_CHANGE\",\n        /**\n         * Fired when Stop Seeking\n         * @constant\n         * @property START_SEEKING\n         * @type {String}\n         */\n        START_SEEKING: \"fr.ina.amalia.player.PlayerEventType.START_SEEKING\",\n        /**\n         * Fired when Stop Seeking\n         * @constant\n         * @property STOP_SEEKING\n         * @type {String}\n         */\n        STOP_SEEKING: \"fr.ina.amalia.player.PlayerEventType.STOP_SEEKING\",\n        /**\n         * Fired when seeking\n         * @constant\n         * @property SEEKING\n         * @type {String}\n         */\n        SEEKING: \"fr.ina.amalia.player.PlayerEventType.SEEKING\"\n    },\n    {});\n"
  },
  {
    "path": "src/player/constants/player-message.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Constant message class\r\n * @class PlayerMessage\r\n * @namespace fr.ina.amalia.player\r\n * @module player\r\n * @submodule player-constants\r\n */\r\n$.Class(\"fr.ina.amalia.player.PlayerMessage\", {\r\n        /**\r\n         * @constant\r\n         * @property PLAYER_OPENSOURCE_LINK\r\n         * @type {String}\r\n         */\r\n        PLAYER_OPENSOURCE_LINK: 'Disponibles en open source.',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_OVERLAY_CONTEXT_MENU_ENABLED_DISABLED_PLUGIN\r\n         * @type {String}\r\n         */\r\n        PLUGIN_OVERLAY_CONTEXT_MENU_ENABLED_DISABLED_PLUGIN: 'Activer/désactiver le plugin overlay.',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_ELEMENTS\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_ELEMENTS: \"éléments.\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_ZOOM_IN\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_ZOOM_IN: \"Zoom avant\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_ZOOM_OUT\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_ZOOM_OUT: \"Zoom arrière\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_CHANGE_DISPLAY\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_CHANGE_DISPLAY: \"Changer la hauteur des lignes.\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_SLIDE_LEFT\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_SLIDE_LEFT: \"Faire défiler vers la gauche\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_SLIDE_RIGHT\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_SLIDE_RIGHT: \"Faire défiler vers la droite\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_SCROLL_UP\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_SCROLL_UP: \"Faire défiler vers le haut\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_SCROLL_DOWN\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_SCROLL_DOWN: \"Faire défiler vers le bas\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_SHOW_HIDE_TOOTIP\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_SHOW_HIDE_TOOTIP: \"Activer/désactiver les infobulles\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_EXPAND\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_EXPAND: \"Maximiser / Minimiser\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_SORT\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_SORT: \"Trier\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_NAV_POINT_PREV\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_NAV_POINT_PREV: \"Aller au marqueur précédent\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_NAV_POINT_NEXT\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_NAV_POINT_NEXT: \"Aller au marqueur suivant\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_TIMEAXIS\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_TIMEAXIS: \"Axe temporel\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_FOCUS\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_FOCUS: \"Zoom\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_CLOSE\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_CLOSE: \"Fermer\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_LIST_EDITOR_LABEL_HEADER\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_LIST_EDITOR_LABEL_HEADER: \"Metadata list editor\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_LABEL_HEADER\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_LABEL_HEADER: \"Metadata block editor\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_LABEL_HEADER\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_ITEMS_EDITOR_LABEL_HEADER: \"Metadata items editor\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_ID\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_ID: \"ID\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_LABAL\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_LABAL: 'Etiquette',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_TYPE\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_TYPE: 'Type',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_TYPE\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_DESCRIPTION: 'Description',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_TYPE\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_REFERENCE: 'Référence',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_AUTHOR\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_AUTHOR: 'Auteur',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_SHAPE\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_SHAPE: 'Forme',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_COLOR\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_COLOR: 'Couleur',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA: 'Vous devez sélectionner au moins un élément dans la liste des métadonnées avant de l\\'ajouter élément.',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA_ITEM_TYPE\r\n         * @type {String}\r\n         */\r\n        PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA_ITEM_TYPE: 'Vous devez définir le type élément.',\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_BIND\r\n         * @type {String}\r\n         */\r\n        PLUGIN_TIMELINE_LABEL_BIND: \"Attacher/Detacher\",\r\n        /**\r\n         * @constant\r\n         * @property PLUGIN_TIMELINE_LABEL_BIND\r\n         * @type {String}\r\n         */\r\n        PLUGIN_D3JS_CHART_LABELS: ['Groupés', 'Empilés']\r\n    },\r\n    {});\r\n"
  },
  {
    "path": "src/player/loader/base-loader.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to load data\n * @class LoaderBase\n * @namespace fr.ina.amalia.player\n * @module player\n * @constructor\n * @param {Object} parameter\n * @param {Object} mediaContainer\n * @param {Object} handlerData\n */\n$.Class(\"fr.ina.amalia.player.BaseLoader\", {}, {\n    /**\n     * Defines configuration\n     * @property settings\n     * @type {Object}\n     * @default {}\n     */\n    settings: {},\n    /**\n     * In charge to render messages in the web console output\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    logger: null,\n    /**\n     * Loaded data\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    data: null,\n    /**\n     * load handler function\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    handlerData: {},\n    /**\n     * complete handler function\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    completeHandler: null,\n    /**\n     * Instance of player\n     * @property player\n     * @type {Object}\n     * @default null\n     */\n    player: null,\n    /**\n     * Wait load event\n     * @property player\n     * @type {Object}\n     * @default null\n     */\n    waitLoadEvent: false,\n    /**\n     * Instance of parser class\n     * @property player\n     * @type {Object}\n     * @default null\n     */\n    parser: null,\n    /**\n     * Init this class\n     * @constructor\n     * @method init\n     * @param {Object} settings\n     */\n    init: function (settings, player, completeHandler, handlerData) {\n        this.settings = $.extend({\n                debug: false,\n                sublocalisations: false\n            },\n            settings || {});\n        this.player = player;\n        this.parser = null;\n        this.handlerData = $.extend({\n                debug: false\n            },\n            handlerData || {});\n        this.completeHandler = completeHandler;\n        this.logger = null;\n        if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n        this.initialize();\n    },\n    /**\n     * initialize\n     * @constructor\n     * @method initialize\n     */\n    initialize: function () {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"initialize\");\n        }\n        this.initializeParser();\n    },\n    /**\n     * In charge to instantiate parser\n     * @method processFilterData\n     * @param {Object} data\n     */\n    initializeParser: function () {\n        this.parser = null;\n        if (this.settings.format === 'json') {\n            this.parser = new fr.ina.amalia.player.parsers.BaseParserMetadata(this.settings);\n        }\n        else {\n            try {\n                // In charge to instantiate parser custom class\n                /* jslint evil: true */\n                this.parser = eval('new ' + this.settings.format + '(this.settings)');\n            }\n            catch (error) {\n                this.parser = null;\n                if (this.logger !== null) {\n                    this.logger.warn(\"Unknown host configuration type.\");\n                    this.logger.error(error.stack);\n                }\n            }\n        }\n    },\n    /**\n     * Set request type\n     * @method setRequestType\n     */\n    setRequestType: function (requestType) {\n        this.requestType = requestType;\n    },\n    /**\n     * Set data type\n     * @method setDataType\n     */\n    setDataType: function (dataType) {\n        this.dataType = dataType;\n    },\n    /**\n     * Set time out value\n     * @method setTimeout\n     */\n    setTimeout: function (timeout) {\n        this.timeout = timeout;\n    },\n    /**\n     * Set send data\n     * @method setSendData\n     */\n    setSendData: function (data) {\n        this.sendData = $.extend(this.sendData, data || {});\n    },\n    /**\n     * Get send data\n     * @method getSendData\n     */\n    getSendData: function () {\n        return this.sendData;\n    },\n    /**\n     * Return wait load state\n     * @method getSendData\n     */\n    getWaitLoadEvent: function () {\n        return this.waitLoadEvent;\n    },\n    /**\n     * Get loaded data\n     * @method initialize\n     */\n    getData: function () {\n        return this.data;\n    },\n    /**\n     * On success\n     * @method onSuccess\n     * @param {Object} data\n     * @param {String} textStatus\n     */\n    onSuccess: function (data, textStatus) {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"onSuccess status: \" + textStatus);\n        }\n    },\n    /**\n     * onError\n     * @method onError\n     * @param {Object} textStatus\n     */\n    onError: function (textStatus) {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"onError status :\" + textStatus);\n        }\n    }\n});\n"
  },
  {
    "path": "src/player/loader/http-loader.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to http loader\n * @class LoaderHTTP\n * @namespace fr.ina.amalia.player\n * @module player\n */\nfr.ina.amalia.player.BaseLoader.extend(\"fr.ina.amalia.player.HttpLoader\", {}, {\n    /**\n     * Load data request type\n     * @property requestType\n     * @type {String}\n     * @default 'GET'\n     */\n    requestType: 'GET',\n    /**\n     * Load data type\n     * @property logger\n     * @type {String}\n     * @default 'json'\n     */\n    dataType: 'json',\n    /**\n     * Send data object\n     * @property sendData\n     * @type {Object}\n     * @default null\n     */\n    sendData: {},\n    /**\n     * call time out\n     * @property timeout\n     * @type {Number}\n     * @default null\n     */\n    timeout: 120000,\n    init: function (settings, player, completeHandler, handlerData) {\n        this._super(settings, player, completeHandler, handlerData);\n        this.waitLoadEvent = true;\n    },\n    /**\n     * initialize\n     * @constructor\n     * @method initialize\n     */\n    initialize: function () {\n        this._super();\n        this.load(this.settings.url);\n    },\n    /**\n     * Load http service\n     * @method load\n     * @param {Object} url\n     */\n    load: function (url) {\n        var self = this;\n        $.ajax({\n            type: this.requestType,\n            url: url,\n            timeout: this.timeout,\n            data: this.sendData,\n            dataType: this.dataType,\n            success: function (data, textStatus) {\n                self.onSuccess(data, textStatus);\n            },\n            error: function (data, textStatus) {\n                self.onError(textStatus);\n            }\n        });\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"Load url :\" + url);\n        }\n    },\n    /**\n     * On success\n     * @method onSuccess\n     * @param {Object} data\n     * @param {String} textStatus\n     */\n    onSuccess: function (data, textStatus) {\n        this._super(data, textStatus);\n        this.data = null;\n        if (this.parser !== null) {\n            this.data = this.parser.processParserData(data);\n            this.player.addAllMetadata(this.data);\n        }\n        if (typeof this.completeHandler === 'function') {\n            this.completeHandler(this.handlerData, textStatus);\n        }\n    },\n    /**\n     * onError\n     * @method onError\n     * @param {Object} textStatus\n     */\n    onError: function (textStatus) {\n        this._super(textStatus);\n        this.data = null;\n        if (typeof this.completeHandler === 'function') {\n            this.completeHandler(this.handlerData, textStatus);\n        }\n    }\n\n});\n"
  },
  {
    "path": "src/player/loader/ws-loader.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to web socket loader\n * @class LoaderHTTP\n * @namespace fr.ina.amalia.player\n * @module player\n */\nfr.ina.amalia.player.BaseLoader.extend(\"fr.ina.amalia.player.WsLoader\", {}, {\n    /**\n     * Defines configuration\n     * @property socket\n     * @type {Object}\n     * @default null\n     */\n    socket: null,\n    listOfMetadata: null,\n    init: function (settings, player, completeHandler, handlerData) {\n        this._super(settings, player, completeHandler, handlerData);\n        this.waitLoadEvent = false;\n    },\n    /**\n     * initialize\n     * @constructor\n     * @method initialize\n     */\n    initialize: function () {\n        this._super();\n        this.socket = null;\n        this.listOfMetadata = [];\n        this.connect(this.settings.url);\n        this.player.getContainer().on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n            self: this\n        }, this.onPlayerDataChange);\n    },\n    /**\n     * In charge to create a new WebSocket\n     */\n    connect: function (url) {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"Load url :\" + url);\n        }\n        // creates a new WebSocket\n        if (typeof WebSocket === \"function\") {\n            this.socket = new WebSocket(url);\n            if (this.socket !== null) {\n                $(this.socket).on('open', {\n                        self: this\n                    },\n                    this.onOpen).on('close', {\n                        self: this\n                    },\n                    this.onClose).on('message', {\n                        self: this\n                    },\n                    this.onMessage).on('error', {\n                        self: this\n                    },\n                    this.onError);\n            }\n        }\n        else {\n            if (this.logger !== null) {\n                this.logger.error('Error: WebSocket is not supported by this browser or the server is not started.');\n            }\n        }\n\n    },\n    /**\n     * This event occurs when socket connection is established\n     * @method onOpen\n     * @param {Object}\n     */\n    onOpen: function (event) {\n        if (event.data.self.logger !== null) {\n            event.data.self.logger.trace(event.data.self.Class.fullName, 'WebSocket connection opened');\n        }\n    },\n    /**\n     * This event occurs when connection is closed.\n     * @method onClose\n     * @param {Object}\n     */\n    onClose: function (event) {\n        event.data.self.socket = null;\n        if (event.data.self.logger !== null) {\n            event.data.self.logger.trace(event.data.self.Class.fullName, 'WebSocket connection closed');\n        }\n    },\n    /**\n     * This event occurs when client receives data from server.\n     * @method onMessage\n     * @param {Object} event\n     */\n    onMessage: function (event) {\n        try {\n            //recived data\n            var jsonData = JSON.parse(event.originalEvent.data);\n            event.data.self.data = event.data.self.parser.processParserData(jsonData);\n            var viewControl = event.data.self.data.viewControl;\n            var action = (event.data.self.data.viewControl !== null && event.data.self.data.viewControl.hasOwnProperty('action')) ? event.data.self.data.viewControl.action : '';\n            //action\n            if (event.data.self.data !== null) {\n                switch (action) {\n                    case 'add-all' :\n                        event.data.self.player.updateBlockMetadata(event.data.self.data.id, {\n                                id: event.data.self.data.id,\n                                label: event.data.self.data.label,\n                                type: event.data.self.data.hasOwnProperty('type') ? event.data.self.data.type : 'default',\n                                author: (viewControl !== null && viewControl.hasOwnProperty('author')) ? viewControl.author : '',\n                                color: (viewControl !== null && viewControl.hasOwnProperty('color')) ? viewControl.color : '#3cf',\n                                shape: (viewControl !== null && viewControl.hasOwnProperty('shape') && viewControl.shape !== \"\") ? viewControl.shape : 'circle'\n                            },\n                            null);\n                        event.data.self.player.addMetadataById(event.data.self.data.id, event.data.self.data.list);\n                        break;\n                    case 'replace-all' :\n                        event.data.self.player.updateBlockMetadata(event.data.self.data.id, {\n                                id: event.data.self.data.id,\n                                label: event.data.self.data.label,\n                                type: event.data.self.data.hasOwnProperty('type') ? event.data.self.data.type : 'default',\n                                author: (viewControl !== null && viewControl.hasOwnProperty('author')) ? viewControl.author : '',\n                                color: (viewControl !== null && viewControl.hasOwnProperty('color')) ? viewControl.color : '#3cf',\n                                shape: (viewControl !== null && viewControl.hasOwnProperty('shape') && viewControl.shape !== \"\") ? viewControl.shape : 'circle'\n                            },\n                            action);\n                        event.data.self.player.replaceAllMetadataById(event.data.self.data.id, event.data.self.data.list);\n                        break;\n                    case 'delete-all' :\n                        event.data.self.player.deleteAllMetadataById(event.data.self.data.id);\n                        break;\n                    default :\n                        event.data.self.player.updateBlockMetadata(event.data.self.data.id, {\n                                id: event.data.self.data.id,\n                                label: event.data.self.data.label,\n                                type: event.data.self.data.hasOwnProperty('type') ? event.data.self.data.type : 'default',\n                                author: (viewControl !== null && viewControl.hasOwnProperty('author')) ? viewControl.author : '',\n                                color: (viewControl !== null && viewControl.hasOwnProperty('color')) ? viewControl.color : '#3cf',\n                                shape: (viewControl !== null && viewControl.hasOwnProperty('shape') && viewControl.shape !== \"\") ? viewControl.shape : 'circle'\n                            },\n                            null);\n                        event.data.self.player.addMetadataById(event.data.self.data.id, event.data.self.data.list);\n                }\n            }\n        }\n        catch (e) {\n            // Exception SyntaxError\n            event.data.self.data = null;\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.error(\"Error to add metadata\");\n                event.data.self.logger.error(event.originalEvent.data);\n            }\n        }\n\n    },\n    /**\n     * This event occurs when there is any error in communication.\n     * @method onError\n     * @param {Object} event\n     */\n    onError: function (event) {\n        event.data.self.socket = null;\n        event.data.self.data = null;\n        if (event.data.self.logger !== null) {\n            event.data.self.logger.error(\"Error to initialize, web socket connection.\");\n            event.data.self.logger.error(event);\n        }\n    },\n    /**\n     * This event occurs when data change event\n     * @method onPlayerDataChange\n     * @param {Object} event\n     * @param {Object} data\n     */\n    onPlayerDataChange: function (event, data) {\n        if (event.data.self.logger !== null) {\n            event.data.self.logger.trace(event.data.self.Class.fullName, 'onPlayerDataChange');\n            event.data.self.logger.debug(event);\n            event.data.self.logger.debug(event.data.self.player.getMetadataById(data.id));\n        }\n    }\n\n});\n"
  },
  {
    "path": "src/player/loaderBase.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to load data\n * @class LoaderBase\n * @namespace fr.ina.amalia.player\n * @module player\n * @constructor\n * @param {Object} parameter\n * @param {Object} mediaContainer\n * @param {Object} handlerData\n */\n$.Class(\"fr.ina.amalia.player.LoaderBase\", {}, {\n    /**\n     * Defines configuration\n     * @property settings\n     * @type {Object}\n     * @default {}\n     */\n    settings: {},\n    /**\n     * In charge to render messages in the web console output\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    logger: null,\n    /**\n     * Load data request type\n     * @property requestType\n     * @type {String}\n     * @default 'GET'\n     */\n    requestType: 'GET',\n    /**\n     * Load data type\n     * @property logger\n     * @type {String}\n     * @default 'json'\n     */\n    dataType: 'json',\n    /**\n     * Send data object\n     * @property sendData\n     * @type {Object}\n     * @default null\n     */\n    sendData: {},\n    /**\n     * call time out\n     * @property timeout\n     * @type {Number}\n     * @default null\n     */\n    timeout: 120000,\n    /**\n     * Loaded data\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    data: null,\n    /**\n     * load handler function\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    handlerData: {},\n    /**\n     * complete handler function\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    completeHandler: null,\n    /**\n     * Init this class\n     * @constructor\n     * @method init\n     * @param {Object} settings\n     */\n    init: function (settings, completeHandler, handlerData) {\n        this.settings = $.extend({\n                debug: false\n            },\n            settings || {});\n        this.handlerData = $.extend({\n                debug: false\n            },\n            handlerData || {});\n        this.completeHandler = completeHandler;\n        if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n        this.initialize();\n    },\n    /**\n     * initialize\n     * @constructor\n     * @method initialize\n     */\n    initialize: function () {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"initialize\");\n        }\n    },\n    /**\n     * Set request type\n     * @method setRequestType\n     */\n    setRequestType: function (requestType) {\n        this.requestType = requestType;\n    },\n    /**\n     * Set data type\n     * @method setDataType\n     */\n    setDataType: function (dataType) {\n        this.dataType = dataType;\n    },\n    /**\n     * Set time out value\n     * @method setTimeout\n     */\n    setTimeout: function (timeout) {\n        this.timeout = timeout;\n    },\n    /**\n     * Set send data\n     * @method setSendData\n     */\n    setSendData: function (data) {\n        this.sendData = $.extend(this.sendData, data || {});\n    },\n    /**\n     * Get send data\n     * @method getSendData\n     */\n    getSendData: function () {\n        return this.sendData;\n    },\n    /**\n     * Get loaded data\n     * @method initialize\n     */\n    getData: function () {\n        return this.data;\n    },\n    /**\n     * Filter loaded data\n     * @method processFilterData\n     * @param {Object} data\n     */\n    processFilterData: function (data) {\n        if (data !== null && typeof data !== \"undefined\") {\n            if (data.hasOwnProperty('status') === true && data.hasOwnProperty('data') === true && data.status === 0 && data !== null) {\n                return data.data;\n            }\n            else if (data.hasOwnProperty('id') === true) {\n                return data;\n            }\n            else {\n                if (this.logger !== null) {\n                    this.logger.warn(this.Class.fullName, \"Error while filtering Data.\");\n                    this.logger.warn(data);\n                }\n            }\n        }\n\n        return null;\n    },\n    /**\n     * Load data\n     * @method load\n     * @param {Object} url\n     */\n    load: function (url) {\n        var self = this;\n        $.ajax({\n            type: this.requestType,\n            url: url,\n            timeout: this.timeout,\n            data: this.sendData,\n            dataType: this.dataType,\n            success: function (data, textStatus) {\n                self.onSuccess(data, textStatus);\n            },\n            error: function (data, textStatus) {\n                self.onError(textStatus);\n            }\n        });\n    },\n    /**\n     * On success\n     * @method onSuccess\n     * @param {Object} data\n     * @param {String} textStatus\n     */\n    onSuccess: function (data, textStatus) {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"onSuccess status: \" + textStatus);\n        }\n        this.data = this.processFilterData(data);\n\n        if (typeof this.completeHandler === 'function') {\n            this.completeHandler(this.handlerData, textStatus);\n        }\n\n    },\n    /**\n     * onError\n     * @method onError\n     * @param {Object} textStatus\n     */\n    onError: function (textStatus) {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"onError status :\" + textStatus);\n        }\n        this.data = null;\n        if (typeof this.completeHandler === 'function') {\n            this.completeHandler(this.handlerData, textStatus);\n        }\n    }\n});\n"
  },
  {
    "path": "src/player/local-storage-manager.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Class en charge de gérer le stockage des données local\n * @class localStorageManager\n * @namespace fr.ina.amalia.player.parsers\n * @module player\n * @constructor\n */\n$.Class(\"fr.ina.amalia.player.LocalStorageManager\", {\n        storageNamespace: 'ina.media.player'\n    },\n    {\n        /**\n         * Defines configuration\n         * @property settings\n         * @type {Object}\n         * @default {}\n         */\n        settings: {},\n        /**\n         * In charge to render messages in the web console output\n         * @property logger\n         * @type {Object}\n         * @default null\n         */\n        logger: null,\n        /**\n         * Local storage data\n         * @property data\n         * @type {Object}\n         * @default null\n         */\n        data: null,\n        /**\n         * Init this class\n         * @constructor\n         * @method init\n         */\n        init: function () {\n            this.initializeStorage();\n        },\n        /**\n         * Initialize local storage data\n         * @method initialize\n         */\n        initializeStorage: function () {\n            if (typeof localStorage !== 'undefined') {\n                try {\n                    if (localStorage.hasOwnProperty(this.Class.storageNamespace) === false) {\n                        localStorage.setItem(this.Class.storageNamespace, JSON.stringify({}));\n                    }\n                    this.data = JSON.parse(localStorage.getItem(this.Class.storageNamespace));\n                }\n                catch (e) {\n                    this.data = null;\n                }\n            }\n            else {\n                this.data = null;\n            }\n        },\n        /**\n         * Update local storage data\n         * @method initialize\n         */\n        updateDataStorage: function () {\n            try {\n                if (typeof localStorage !== 'undefined') {\n                    localStorage.setItem(this.Class.storageNamespace, JSON.stringify(this.data));\n                }\n            }\n            catch (e) {\n                this.data = null;\n            }\n\n        },\n        /**\n         * Method check if has key\n         * @method hasItem\n         * @param {String} key\n         */\n        hasItem: function (key) {\n            return (this.data !== null && this.data.hasOwnProperty(key) === true);\n        },\n        /**\n         * Return key data\n         * @method getItem\n         * @param {String} key\n         */\n        getItem: function (key) {\n            if (this.data !== null && this.data.hasOwnProperty(key) === true) {\n                return this.data[key];\n            }\n            return null;\n        },\n        /**\n         * Set item with key and value\n         * @method setItem\n         * @param {String} key\n         * @param {String} value\n         */\n        setItem: function (key, value) {\n            try {\n                if (this.data !== null && typeof key !== 'undefined' && typeof value !== 'undefined') {\n                    this.data[key] = value;\n                    this.updateDataStorage();\n                }\n            }\n            catch (e) {\n                return null;\n            }\n            return true;\n        },\n        /**\n         * Remove item with key\n         * @method removeItem\n         * @param {String} key\n         */\n        removeItem: function (key) {\n            if (this.data !== null && typeof key !== 'undefined') {\n                this.data.splice(key, 1);\n                this.updateDataStorage();\n            }\n        },\n        /**\n         * Clear all data\n         * @method clear\n         */\n        clear: function () {\n            if (typeof localStorage !== 'undefined') {\n                localStorage.removeItem(this.Class.storageNamespace);\n            }\n        }\n    });\n"
  },
  {
    "path": "src/player/media-factory.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Media factory\r\n * @class MediaFactory\r\n * @namespace fr.ina.amalia.player\r\n * @module player\r\n * @param {Object} mediaContainer\r\n * @param {Object} settings\r\n * @constructor\r\n */\r\n$.Class(\"fr.ina.amalia.player.MediaFactory\", {}, {\r\n    /**\r\n     * Defines configuration\r\n     * @property settings\r\n     * @type {Object}\r\n     * @default {}\r\n     */\r\n    settings: {},\r\n    /**\r\n     * In charge to render messages in the web console output\r\n     * @property logger\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    logger: null,\r\n    /**\r\n     * Player dom element\r\n     * @property mediaContainer\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    mediaContainer: null,\r\n    /**\r\n     * Player instance\r\n     * @property player\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    player: null,\r\n    /**\r\n     * Error message container\r\n     * @property errorContainer\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    errorContainer: null,\r\n\r\n    /**\r\n     * Last Seek time\r\n     * @property lastSeekTime\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    lastSeekTime: null,\r\n\r\n\r\n    /**\r\n     * Init this class\r\n     * @constructor\r\n     * @method init\r\n     * @param {Object} mediaContainer\r\n     * @param {Object} settings\r\n     */\r\n    init: function (mediaContainer, settings) {\r\n        this.mediaContainer = $(mediaContainer);\r\n        this.lastSeekTime = 0;\r\n        this.settings = $.extend({\r\n                player: 'default',\r\n                src: null,\r\n                poster: \"\",\r\n                autoplay: false,\r\n                plugins: {},\r\n                callbacks: {},\r\n                duration: null,\r\n                debug: false\r\n            },\r\n            settings || {});\r\n        if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\r\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\r\n                enabled: this.settings.debug\r\n            });\r\n        }\r\n        this.initialize();\r\n    },\r\n    /**\r\n     * Initialize\r\n     * @method initialize\r\n     * @throws {Object}\r\n     */\r\n    initialize: function () {\r\n        try {\r\n            if (this.settings.src !== null) {\r\n                this.loadPlayer();\r\n                if (this.logger !== null) {\r\n                    this.logger.trace(this.Class.fullName, \"initialize\");\r\n                }\r\n            }\r\n            else {\r\n                this.createErrorContainer();\r\n                this.setErrorCode(fr.ina.amalia.player.PlayerErrorCode.MEDIA_FILE_NOT_FOUND);\r\n            }\r\n        }\r\n        catch (error) {\r\n            this.createErrorContainer();\r\n            this.setErrorCode(fr.ina.amalia.player.PlayerErrorCode.ERROR_HTML5_SUPPORT);\r\n            if (this.logger !== null) {\r\n                this.logger.error(error.stack);\r\n            }\r\n        }\r\n    },\r\n    /**\r\n     * Method for create error container elements.\r\n     * @method createErrorContainer\r\n     */\r\n    createErrorContainer: function () {\r\n        this.errorContainer = $('<div>', {\r\n            'class': 'ajs-error'\r\n        });\r\n        this.errorContainer.hide();\r\n        this.mediaContainer.append(this.errorContainer);\r\n    },\r\n    /**\r\n     * Set error code\r\n     * @method setErrorCode\r\n     * @param {Object} errorCode\r\n     */\r\n    setErrorCode: function (errorCode) {\r\n        if (typeof this.errorContainer !== \"undefined\") {\r\n            var messageContainer = $('<p>', {\r\n                text: fr.ina.amalia.player.PlayerErrorCode.getMessage(errorCode)\r\n            });\r\n            this.errorContainer.html(messageContainer);\r\n            this.errorContainer.show();\r\n        }\r\n    },\r\n    /**\r\n     * Load player\r\n     * @method loadPlayer\r\n     */\r\n    loadPlayer: function () {\r\n\r\n        if (this.settings.player === \"default\") {\r\n            this.loadHtml5MediaPlayer();\r\n        }\r\n        else {\r\n            /* jslint evil: true */\r\n            this.player = eval(\"new \" + this.settings.player + \"(this.settings, this.mediaContainer);\");\r\n        }\r\n    },\r\n    /**\r\n     * Load player html5\r\n     * @method loadHtml5MediaPlayer\r\n     */\r\n    loadHtml5MediaPlayer: function () {\r\n        if (this.logger !== null) {\r\n            this.logger.info(\"Load html 5 media player\");\r\n        }\r\n        var browserFeatureDetection = new fr.ina.amalia.player.helpers.BrowserFeatureDetection();\r\n        if (browserFeatureDetection.isSupportsVideos()) {\r\n            this.player = new fr.ina.amalia.player.PlayerHtml5(this.settings, this.mediaContainer);\r\n        }\r\n        else {\r\n            if (this.logger !== null) {\r\n                this.logger.error(\"Your browser does not support the video tag.\");\r\n            }\r\n            throw new Error(\"Your browser does not support the video tag.\");\r\n        }\r\n    },\r\n    /**\r\n     * Return player instance\r\n     * @method getPlayer\r\n     * @returns {Object}\r\n     */\r\n    getPlayer: function () {\r\n        return this.player;\r\n    }\r\n});\r\n"
  },
  {
    "path": "src/player/media-type-dash-mpeg.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Desh-mpeg streaming\n * @class DashMpeg\n * @namespace fr.ina.amalia.player.media.type\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer player instance\n */\nfr.ina.amalia.player.mediaTypeManager.extend(\"fr.ina.amalia.player.media.type.DashMpeg\", {}, {\n    /**\n     * Dash context\n     * @property context\n     * @type {Object}\n     * @default null\n     */\n    context: null,\n    /**\n     * Dash player instance\n     * @property context\n     * @type {Object}\n     * @default null\n     */\n    dashPlayer: null,\n    /**\n     * Initialize dash context\n     * @method initialize\n     */\n    initialize: function () {\n        this.context = new Dash.di.DashContext();\n        this.dashPlayer = new MediaPlayer(this.context);\n    },\n    /**\n     * Set media source file (mdp file)\n     * @param {String} url\n     * @method setSrc\n     */\n    setSrc: function (url) {\n        this.dashPlayer.setAutoPlay(this.settings.autoplay);\n        this.dashPlayer.startup();\n        this.dashPlayer.attachView(this.mediaPlayer.get(0));\n        // Fetches and parses the manifest - WARNING the callback is non-standard \"error-last\" style\n        this.dashPlayer.retrieveManifest(url, $.proxy(this.initializeDashJS, this));\n        this.dashPlayer.videoElementExt = this.mediaPlayer.get(0);\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"set dash src :\" + this.url);\n        }\n    },\n    initializeDashJS: function (manifest, err) {\n        if (typeof err === \"object\") {\n            this.mainObj.setErrorCode(fr.ina.amalia.player.PlayerErrorCode.ERROR_MANIFEST_DASH);\n        }\n        else {\n            // Attach the source with any protection data\n            this.dashPlayer.attachSource(manifest);\n        }\n    }\n\n});\n"
  },
  {
    "path": "src/player/media-type-manager.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class for media type file\n * @class mediaTypeManager\n * @namespace fr.ina.amalia.player.media.type\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer player instance\n */\n$.Class(\"fr.ina.amalia.player.mediaTypeManager\", {}, {\n    /**\n     * Instance of Player HTML5\n     * @property mediaPlayer\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    mediaPlayer: null,\n    /**\n     * Instance of main class\n     * @property mainObj\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    mainObj: null,\n\n    /**\n     * logger instance\n     * @property logger\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    logger: null,\n    /**\n     * Configuration\n     * @property settings\n     * @type {Object}\n     * @default \"{}\"\n     */\n    settings: {},\n    init: function (settings, mainObj) {\n        this.mediaPlayer = mainObj.mediaPlayer;\n        this.mainObj=mainObj;\n        this.namespace = this.Class.fullName;\n        this.settings = $.extend({\n                debug: false,\n                internalPlugin: false\n            },\n            settings || {});\n        if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n        if (this.mediaPlayer === null) {\n            throw new Error(\"Can't initialize plugin name\" + this.Class.fullName);\n        }\n        this.initialize();\n    },\n    /**\n     * initialize\n     * @method initialize\n     */\n    initialize: function () {\n\n    }\n});\n"
  },
  {
    "path": "src/player/metadata/localisation-manager.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * For manage localisation block\n * @class LocalisationManager\n * @namespace fr.ina.amalia.player\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer player instance\n */\n$.Class(\"fr.ina.amalia.player.LocalisationManager\", {}, {\n    /**\n     * Logger instance\n     * @property logger\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    logger: null,\n    /**\n     * Instance of Player HTML5\n     * @property mediaPlayer\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    localisation: null,\n    /**\n     * Init this class\n     * @constructor\n     * @method init\n     * @param {Object} settings\n     */\n    init: function () {\n        this.localisation = [];\n    },\n    /**\n     * in charge to set localisation\n     * @param {type} localisation\n     */\n    setLoc: function (localisation) {\n        this.localisation = localisation;\n    },\n    /**\n     * Return localisation tcin tcout\n     * @returns return timecode\n     */\n    getLocTc: function () {\n        if (this.localisation !== null && this.localisation.length > 1) {\n            //sort by tc\n            this.localisation.sort(function (obj1, obj2) {\n                obj1.tc = (typeof obj1.tc === \"number\") ? obj1.tc : parseFloat(fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(obj1.tc));\n                obj2.tc = (typeof obj2.tc === \"number\") ? obj2.tc : parseFloat(fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(obj2.tc));\n                delete obj1.firstItem;\n                delete obj1.lastItem;\n                delete obj1.tcRange;\n                return obj1.tc - obj2.tc;\n            });\n            var _firstItem = this.localisation[0];\n            var _lastItem = this.localisation[this.localisation.length - 1];\n\n            //add range\n            for (var i = 0;\n                 i < this.localisation.length;\n                 i++) {\n                var item = this.localisation[i];\n                item.tcRange = {\n                    min: _firstItem.tc,\n                    max: _lastItem.tc\n                };\n                item.subItem = true;\n            }\n            if (_firstItem.hasOwnProperty('tc') && _lastItem.hasOwnProperty('tc')) {\n                _firstItem.firstItem = true;\n                _lastItem.lastItem = true;\n                delete _firstItem.range;\n                delete _lastItem.range;\n                _firstItem.tcRange = {\n                    max: _lastItem.tc\n                };\n                _lastItem.tcRange = {\n                    min: _firstItem.tc\n                };\n                return {\n                    tcin: _firstItem.tc,\n                    tcout: _lastItem.tc\n                };\n            }\n        }\n        return null;\n    },\n    /**\n     * In  charge to updata localisation block for spatial data\n     * @method updateSpacialLocBlock\n     * @param {Object} localisations\n     */\n    updateSpacialLocBlock: function (localisations) {\n        if (localisations !== null && localisations.length > 0) {\n            for (var i = 0;\n                 i < localisations.length;\n                 i++) {\n                var loc = localisations[i];\n                if (loc.hasOwnProperty('sublocalisations') && typeof loc.sublocalisations === \"object\" && loc.sublocalisations !== null) {\n                    if (loc.sublocalisations.localisation.length > 0) {\n                        loc.sublocalisations.localisation.sort(function (obj1, obj2) {\n                            return obj1.tc - obj2.tc;\n                        });\n                        for (var j = 0;\n                             j < loc.sublocalisations.localisation.length;\n                             j++) {\n                            var item = loc.sublocalisations.localisation[j];\n                            if (item.tc === null && item.deleted === true) {\n                                loc.sublocalisations.localisation.splice(j, 1);\n                            }\n                        }\n                        loc.sublocalisations.localisation.sort(function (obj1, obj2) {\n                            return obj1.tc - obj2.tc;\n                        });\n                        if (loc.sublocalisations.localisation.length === 1) {\n                            var _tmpItem = loc.sublocalisations.localisation[0];\n                            loc.tc = parseFloat(_tmpItem.tc);\n                            loc.shape = $.extend({}, _tmpItem.shape);\n                            loc.sublocalisations = null;\n                            delete (loc.tcin);\n                            delete (loc.tcout);\n                            return true;\n                        }\n                        else if (loc.sublocalisations.localisation.length > 1) {\n                            var _firstItem = loc.sublocalisations.localisation[0];\n                            var _lastItem = loc.sublocalisations.localisation[loc.sublocalisations.localisation.length - 1];\n                            if (_firstItem.hasOwnProperty('tc') && _lastItem.hasOwnProperty('tc')) {\n                                loc.tc = parseFloat(_firstItem.tc);\n                                loc.tcin = parseFloat(_firstItem.tc);\n                                loc.tcout = parseFloat(_lastItem.tc);\n                                return true;\n                            }\n                        }\n                    }\n                }\n                else if (loc.hasOwnProperty('delete') && loc.delete === true) {\n                    localisations.splice(i, 1);\n                }\n            }\n        }\n        return false;\n    },\n    /**\n     * In  charge to updata localisation block for spatial data\n     * @method updateLocBlock\n     * @param {Object} localisations\n     */\n    updateLocBlock: function (localisations) {\n        if (localisations !== null && localisations.length > 0) {\n            for (var i = localisations.length - 1;\n                 i >= 0;\n                 i--) {\n                var loc = localisations[i];\n                if (loc.tc === null && loc.tcin === null) {\n                    localisations.splice(i, 1);\n                }\n                else if (loc.hasOwnProperty('sublocalisations') && loc.sublocalisations !== null && typeof loc.sublocalisations === \"object\" && loc.sublocalisations.localisation.length > 0) {\n                    for (var j = loc.sublocalisations.localisation.length - 1; j >= 0; j--) {\n                        var item = loc.sublocalisations.localisation[j];\n                        if (item.tc === null && item.hasOwnProperty('deleted') && item.deleted === true) {\n                            loc.sublocalisations.localisation.splice(j, 1);\n                        }\n                    }\n                }\n            }\n        }\n        return false;\n    },\n    /**\n     * In  charge to shift localisation block with spatial data\n     * @method shiftSpacialLocBlock\n     * @param {Object} localisations\n     */\n    shiftSpacialLocBlock: function (loc, tcin) {\n        var shiftTc = tcin - loc.tcin;\n        loc.tcout += shiftTc;\n\n        if (loc.hasOwnProperty('sublocalisations') && typeof loc.sublocalisations === \"object\") {\n            if (loc.sublocalisations.localisation.length > 0) {\n                loc.sublocalisations.localisation.sort(function (obj1, obj2) {\n                    return obj1.tc - obj2.tc;\n                });\n                for (var j = 0;\n                     j < loc.sublocalisations.localisation.length;\n                     j++) {\n                    var item = loc.sublocalisations.localisation[j];\n                    item.tc += shiftTc;\n                }\n\n            }\n            return true;\n        }\n        return false;\n    },\n    /**\n     * In charge to shift localisation with tc\n     * @method shiftLocBlock\n     * @param {Object} localisations\n     */\n    shiftLocBlock: function (metadata, shiftTc, tcin, tcout, selected) {\n        if (typeof metadata === \"object\" && metadata.length > 0) {\n            metadata.sort(function (obj1, obj2) {\n                return obj1.tc - obj2.tc;\n            });\n            for (var j = 0;\n                 j < metadata.length;\n                 j++) {\n                var item = metadata[j];\n                if ((selected === false) || (selected === true && item.hasOwnProperty('selected') && item.selected === true)) {\n                    if (item.hasOwnProperty('tc') && item.tc !== null) {\n                        //item.tc = Math.min(Math.max(tcin, item.tc + shiftTc), tcout);\n                        item.tc += shiftTc;\n                    }\n                    if (item.hasOwnProperty('tcin') && item.tcin !== null) {\n                        item.tcin += shiftTc;\n                    }\n                    if (item.hasOwnProperty('tcout') && item.tcout !== null) {\n                        item.tcout += shiftTc;\n                    }\n                }\n\n            }\n            return true;\n        }\n        return false;\n    }\n});\n"
  },
  {
    "path": "src/player/metadata/metadata-manager.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage all metadata\n * @class MetadataManager\n * @namespace fr.ina.amalia.player\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer player instance\n */\n$.Class(\"fr.ina.amalia.player.MetadataManager\", {}, {\n    /**\n     * Instance of Player HTML5\n     * @property mediaPlayer\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    mediaPlayer: null,\n    /**\n     * Logger instance\n     * @property logger\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    logger: null,\n    /**\n     * Configuration\n     * @property settings\n     * @type {Object}\n     * @default \"{}\"\n     */\n    settings: {},\n    /**\n     * Contains list of blocks metadata\n     * @property listOfBlocks\n     * @type {Object}\n     * @default \"[]\"\n     */\n    listOfBlocks: {},\n    /**\n     * List of metadata with id\n     * @property listOfMetadata\n     * @type {Object}\n     * @default null\n     */\n    listOfMetadata: [],\n    /**\n     * Contains list of selected items\n     * @property listOfSelectedItems\n     * @type {Object}\n     * @default \"[]\"\n     */\n    listOfSelectedItems: [],\n    /**\n     * Selected metadata id\n     * @property selectedMetadataId\n     * @type {Number}\n     * @default 0\n     */\n    selectedMetadataId: null,\n    /**\n     * Init\n     * @method initialize\n     */\n    init: function (settings, mediaPlayer) {\n        this.mediaPlayer = mediaPlayer;\n        this.listOfBlocks = {};\n        this.listOfMetadata = [];\n        this.listOfSelectedItems = [];\n        this.selectedMetadataId = null;\n        this.selectedMetadataId = null;\n        // Settings\n        this.settings = $.extend({\n            debug: false,\n            internalPlugin: false\n        }, settings || {});\n        // Logger\n        if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n        if (this.mediaPlayer === null) {\n            throw new Error(\"Can't initialize plugin name\" + this.Class.fullName);\n        }\n\n    },\n\n    /**\n     * Return the block of metadata\n     * @method getBlocksMetadata\n     * @return Array return the list of blocks with data\n     */\n    getBlocksMetadata: function () {\n        try {\n            var data = {};\n            for (var key in this.listOfBlocks) {\n                if (this.listOfBlocks.hasOwnProperty(key)) {\n                    var d = JSON.parse(JSON.stringify(this.listOfBlocks[key]));\n                    if (typeof this.listOfMetadata[key] !== 'undefined') {\n                        d.localisation = JSON.parse(JSON.stringify(this.listOfMetadata[key], function (key, value) {\n                            if (key === \"refLoc\") {\n                                return undefined;\n                            }\n                            else {\n                                return value;\n                            }\n                        }));\n                    }\n                    else {\n                        d.localisation = null;\n                    }\n                    data[key] = d;\n                }\n            }\n            return data;\n        }\n        catch (error) {\n            if (this.logger !== null) {\n                this.logger.warn(\"Error to return blocks\");\n                this.logger.error(error.stack);\n            }\n        }\n        return null;\n    },\n    /**\n     * Return the block of metadata\n     * @method getBlockMetadata\n     * @return Array return block without data\n     */\n    getBlockMetadata: function (id) {\n        var data = null;\n        try {\n            if (typeof this.listOfBlocks[id] !== 'undefined') {\n                data = this.listOfBlocks[id];\n                if (typeof this.listOfMetadata[id] !== 'undefined') {\n                    data.localisation = JSON.parse(JSON.stringify(this.listOfMetadata[id], function (key, value) {\n                        return (key === \"refLoc\") ? undefined : value;\n                    }));\n                }\n                else {\n                    data.localisation = null;\n                }\n            }\n        }\n        catch (error) {\n            if (this.logger !== null) {\n                this.logger.warn(\"Error to return blocks\");\n                this.logger.error(error.stack);\n            }\n        }\n        return data;\n    },\n    /**\n     * In charge to update the block metadata with\n     * @method updateBlockMetadata\n     * @param id id of metadata\n     * @param data block data\n     * @param action trigger action\n     */\n    updateBlockMetadata: function (id, data, action) {\n        try {\n            if (typeof data === \"object\") {\n                this.listOfBlocks[id] = $.extend(this.listOfBlocks[id], JSON.parse(JSON.stringify(data)));\n            } else if (typeof data === \"string\") {\n                this.listOfBlocks[id] = $.extend(this.listOfBlocks[id], JSON.parse(data));\n            }\n\n            if (typeof this.listOfMetadata[id] === 'undefined') {\n                this.listOfMetadata[id] = [];\n            }\n            if (this.listOfBlocks[id].hasOwnProperty('localisation') && this.listOfBlocks[id].localisation) {\n                this.listOfMetadata[id] = this.listOfBlocks[id].localisation;\n            }\n            this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: id,\n                action: action\n            });\n        }\n        catch (error) {\n            if (this.logger !== null) {\n                this.logger.warn(\"Error to add blocks\");\n                this.logger.error(error.stack);\n            }\n        }\n    },\n    /**\n     * Remove the block metadata with id metadata\n     * @method removeBlockMetadata\n     * @param {String} id\n     */\n    removeBlockMetadata: function (id) {\n        return delete (this.listOfBlocks[id]);\n    },\n    /**\n     * In charge to add metadata, it is called by metadata parser\n     * @method addAllMetadata\n     * @param {Object} parsedData\n     * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n     */\n    addAllMetadata: function (parsedData) {\n\n        if (typeof parsedData === \"object\" && parsedData !== null) {\n            if (typeof parsedData.length !== \"undefined\") {\n                for (var d in parsedData) {\n                    if (parsedData[d].hasOwnProperty('id')) {\n                        if (typeof this.listOfMetadata[parsedData[d].id] === 'undefined') {\n                            this.listOfMetadata[parsedData[d].id] = [];\n                        }\n\n                        for (var j = 0;\n                             j < parsedData[d].list.length;\n                             j++) {\n                            var obj = parsedData[d].list[j];\n                            this.listOfMetadata[parsedData[d].id].push(obj);\n                        }\n                        if (parsedData[d].hasOwnProperty('type') === true) {\n                            var _data = parsedData[d];\n                            this.updateBlockMetadata(_data.id, {\n                                type: _data.type,\n                                id: _data.id,\n                                viewControl: _data.hasOwnProperty('viewControl') ? _data.viewControl : null,\n                                label: _data.hasOwnProperty('label') ? _data.label : _data.id\n                            });\n                        }\n                        else {\n                            this.updateBlockMetadata(parsedData[d].id, {});\n                        }\n                        this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                            id: parsedData[d].id\n                        });\n                        if (this.logger !== null) {\n                            this.logger.info(\"AddAllMetadata ID:\" + parsedData[d].id);\n                        }\n                    }\n                    else {\n                        if (this.logger !== null) {\n                            this.logger.warn(\"parseData : Error to find data type.\");\n                        }\n                    }\n                }\n            }\n            else {\n                if (typeof this.listOfMetadata[parsedData.id] === 'undefined') {\n                    this.listOfMetadata[parsedData.id] = [];\n                }\n\n                for (var k = 0;\n                     k < parsedData.list.length;\n                     k++) {\n                    var o = parsedData.list[k];\n                    this.listOfMetadata[parsedData.id].push(o);\n                }\n                this.updateBlockMetadata(parsedData.id, {\n                    id: parsedData.id,\n                    label: parsedData.hasOwnProperty('label') ? parsedData.label : parsedData.id,\n                    type: parsedData.hasOwnProperty('type') ? parsedData.type : '',\n                    viewControl: parsedData.hasOwnProperty('viewControl') ? parsedData.viewControl : null\n                });\n                this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                    id: parsedData.id\n                });\n                if (this.logger !== null) {\n                    this.logger.info(\"Add Metadata ID:\" + parsedData.id);\n                }\n            }\n        }\n        else {\n            if (this.logger !== null) {\n                this.logger.warn(\"Error to add metadata.\");\n                this.logger.warn(parsedData);\n            }\n        }\n    },\n    /**\n     * In charge to return metadata by id\n     * @method addMetadataById\n     * @param {Object} data\n     * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n     */\n    addMetadataById: function (id, parsedData) {\n        if (typeof this.listOfMetadata[id] === 'undefined') {\n            this.listOfMetadata[id] = [];\n            this.updateBlockMetadata(id, {});\n        }\n        if (typeof parsedData === \"object\") {\n            for (var j = 0;\n                 j < parsedData.length;\n                 j++) {\n                var obj = parsedData[j];\n                this.listOfMetadata[id].push(obj);\n            }\n            this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: id\n            });\n        }\n    },\n    /**\n     * In charge to replace metadata by id\n     * @method replaceAllMetadataById\n     * @param {Object} data\n     * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n     */\n    replaceAllMetadataById: function (id, parsedData) {\n        this.listOfMetadata[id] = [];\n        if (typeof parsedData === \"object\") {\n            this.listOfMetadata[id] = [];\n            for (var j = 0;\n                 j < parsedData.length;\n                 j++) {\n                var obj = parsedData[j];\n                this.listOfMetadata[id].push(obj);\n            }\n            this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: id\n            });\n        }\n    },\n    /**\n     * In charge to delete metadata by id\n     * @method deleteAllMetadataById\n     * @param {Object} data\n     * @event fr.ina.amalia.player.PlayerEventType.DATA_CHANGE\n     */\n    deleteAllMetadataById: function (id) {\n        if (typeof this.listOfMetadata[id] !== \"undefined\") {\n            var deleted = false;\n            if (this.listOfMetadata.hasOwnProperty(id)) {\n                /* jslint evil: true */\n                deleted = eval(\"delete this.listOfMetadata['\" + id + \"']\");\n            }\n            if (deleted) {\n                this.removeBlockMetadata(id);\n                this.selectedMetadataId = null;\n                this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                    metadataId: this.selectedMetadataId\n                });\n                this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                    id: id,\n                    action: 'deleteBlock'\n                });\n            }\n        }\n        return false;\n    },\n    /**\n     * In charge to add metadata item and return reference of the pushed element\n     * @method addMetadataItem\n     */\n    addMetadataItem: function (metadataId, data) {\n        if (typeof this.listOfMetadata[metadataId] !== 'object') {\n            this.listOfMetadata[metadataId] = [];\n        }\n\n        if (typeof data === \"object\") {\n            var len = this.listOfMetadata[metadataId].push(data);\n            var refData = this.listOfMetadata[metadataId][len - 1];\n            this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: metadataId\n            });\n            return refData;\n        }\n    },\n\n    /**\n     * Return metadata by id\n     * @method getMetadataById\n     * @param {String} id\n     * @return {Array}\n     */\n    getMetadataById: function (id) {\n        return (typeof this.listOfMetadata[id] === 'undefined') ? null : this.listOfMetadata[id];\n    },\n    /**\n     * Set metadata by id\n     * @method setMetadataById\n     * @param {String} id\n     * @param {Object} data\n     * @return {Array}\n     */\n    setMetadataById: function (id, data) {\n        if (typeof data === \"object\") {\n            this.listOfMetadata[id] = data;\n        }\n        else if (typeof data === \"string\") {\n            try {\n                var jsonData = JSON.parse(data);\n                this.listOfMetadata[id] = jsonData;\n            }\n            catch (e) {\n                return false;\n            }\n        }\n        else {\n            return false;\n        }\n        this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n            id: id\n        });\n        return true;\n    },\n    /**\n     * In charge to remove metadata\n     * @method removeMetadataById\n     */\n    removeMetadataById: function (id) {\n        this.listOfMetadata[id] = [];\n        this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n            id: id\n        });\n    },\n    /**\n     * Return a metadata by specified time code.\n     * @method getMetadataWithRange\n     * @param id\n     * @param tcin\n     * @param tcout\n     * @return {Array}\n     */\n    getMetadataWithRange: function (id, tcin, tcout) {\n        var metadatas = this.getMetadataById(id);\n\n        var results = [];\n        if (metadatas !== null && typeof metadatas === \"object\") {\n            results = $.grep(metadatas, function (n) {\n                return (n.hasOwnProperty('tc') && n.tc !== null) ? (n.tc >= tcin && n.tc <= tcout) : true;\n            });\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"getMetadataWithRange Id:\" + id + \" Tcin:\" + tcin + \" Tcout:\" + tcout + \" Number of Elements:\" + metadatas.length);\n            }\n        }\n        else {\n            if (this.logger !== null) {\n                this.logger.warn(\"GetMetadataWithRange:  No data id:\" + id);\n            }\n        }\n\n        return results;\n    },\n    /** selected metadata* */\n    /**\n     * Return selected component id\n     * @method getSelectedMetadataId\n     */\n    getSelectedMetadataId: function () {\n        return (this.selectedMetadataId !== null) ? this.selectedMetadataId.toString() : null;\n    },\n    /**\n     * Set selected component id\n     * @method setSelectedMetadataId\n     */\n    setSelectedMetadataId: function (metadataId) {\n        if (this.selectedMetadataId !== metadataId) {\n            this.removeAllSelectedItems();\n            this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: this.selectedMetadataId\n            });\n            this.selectedMetadataId = metadataId;\n            this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                metadataId: this.selectedMetadataId\n            });\n        }\n    },\n    /** selected items* */\n    /**\n     * Return selected items\n     * @method getSelectedItems\n     * @return {Array}\n     */\n    getSelectedItems: function () {\n        return this.listOfSelectedItems;\n    },\n    /**\n     * In charge to selected item\n     * @method addSelectedItem\n     * @param item\n     */\n    addSelectedItem: function (item) {\n        if (item !== null && typeof item === \"object\") {\n            item.selected = true;\n\n\n        }\n        this.listOfSelectedItems.push(item);\n        this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.SELECTED_ITEMS_CHANGE, {item: item});\n    },\n    /**\n     * In charge to clear selected iteme\n     * @method removeSelectedItem\n     * @param idx\n     */\n    removeSelectedItem: function (idx) {\n        if (this.listOfSelectedItems[idx] !== 'undefined' && isNaN(idx) === false) {\n            var data = this.listOfSelectedItems[idx];\n            if (data !== null && typeof data === \"object\" && data.hasOwnProperty('selected') === true) {\n                data.selected = false;\n            }\n            if (idx > -1) {\n                this.listOfSelectedItems.splice(idx, 1);\n            }\n        }\n    },\n    /**\n     * In charge to remove all selected items\n     * @method removeAllSelectedItems\n     */\n    removeAllSelectedItems: function () {\n        if (typeof this.listOfSelectedItems !== 'undefined' && this.listOfSelectedItems.hasOwnProperty('length') && this.listOfSelectedItems.length > 0) {\n            for (var i = 0;\n                 i < this.listOfSelectedItems.length;\n                 i++) {\n                var data = this.listOfSelectedItems[i];\n\n                if (data !== null && typeof data === \"object\" && data.hasOwnProperty('selected') === true) {\n                    data.selected = false;\n\n                }\n            }\n        }\n        this.listOfSelectedItems = [];\n    }\n});\n"
  },
  {
    "path": "src/player/parsers/base-parser-metadata.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to parse metadata (json)\n * @class MetadataParser\n * @namespace fr.ina.amalia.player.parsers\n * @module player\n * @constructor\n * @param {Object} settings Defines the configuration of this class\n */\n$.Class(\"fr.ina.amalia.player.parsers.BaseParserMetadata\", {}, {\n    /**\n     * Defines configuration\n     * @property settings\n     * @type {Object}\n     * @default {}\n     */\n    settings: {},\n    /**\n     * In charge to render messages in the web console output\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    logger: null,\n    /**\n     * Parsed data\n     * @property data\n     * @type {Object}\n     * @default null\n     */\n    data: null,\n    /**\n     * Init this class\n     * @constructor\n     * @method init\n     * @param {Object} settings\n     */\n    init: function (settings) {\n        this.settings = $.extend({\n                debug: false,\n                mainLevel: false\n            },\n            settings || {});\n        if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n    },\n    /**\n     * Filter loaded data\n     * @method processFilterData\n     * @param {Object} data\n     */\n    processParserData: function (data) {\n        if (data !== null && typeof data !== \"undefined\") {\n            if (data.hasOwnProperty('status') === true && data.hasOwnProperty('data') === true && data.status === 0 && data !== null) {\n                return this.parseData(data.data);\n            }\n            else if (data.hasOwnProperty('id') === true) {\n                return this.parseData(data);\n            }\n            else if (data.length !== \"undefined\") {\n                return this.parseData(data);\n            }\n        }\n        if (this.logger !== null) {\n            this.logger.warn(this.Class.fullName, \"Error while filtering Data.\");\n            this.logger.warn(data);\n        }\n        return null;\n    },\n    /**\n     * Parse data\n     * @method parseData\n     * @param {Object} data\n     * @returns {metadataParserAnonym$1.parseData@call;getData|Array}\n     */\n    parseData: function (data) {\n        var parsedMetadata = null;\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"parseData\");\n        }\n        // if data has many entity\n        if (typeof data.length !== \"undefined\") {\n            parsedMetadata = [];\n            for (var d in data) {\n                if (typeof data[d] === \"string\") {\n                    try {\n                        data[d] = JSON.parse(data[d]);\n                        parsedMetadata.push(this.getData(data[d]));\n                    } catch (e) {\n                        if (this.logger !== null) {\n                            this.logger.warn(\"parseData : Error to find data type.\");\n                        }\n                    }\n                } else if (data[d].hasOwnProperty('id') === true) {\n                    parsedMetadata.push(this.getData(data[d]));\n                }\n                else {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"parseData : Error to find data type.\");\n                    }\n                }\n            }\n        }\n        else if (data.hasOwnProperty('id') === true) {\n            parsedMetadata = this.getData(data);\n        }\n        else {\n            if (this.logger !== null) {\n                this.logger.warn(\"parseData : Error to find data type.\");\n            }\n        }\n        return parsedMetadata;\n    },\n    /**\n     * Return parsed localisations data\n     * @method methodName\n     * @param {Object} data\n     * @returns {Array}\n     */\n    getData: function (data) {\n        var parsedMetadata = null;\n        parsedMetadata = {};\n        parsedMetadata.id = data.id;\n        parsedMetadata.type = data.type;\n        parsedMetadata.viewControl = data.hasOwnProperty('viewControl') ? data.viewControl : null;\n        parsedMetadata.label = data.hasOwnProperty('label') ? data.label : data.id;\n        parsedMetadata.list = [];\n        if (data.hasOwnProperty('localisation') === true && data.localisation !== null && data.localisation.length > 0) {\n            var localisations = null;\n            if (this.settings.hasOwnProperty('parameters') && this.settings.parameters !== null && this.settings.parameters.hasOwnProperty('mainLevel') && this.settings.parameters.mainLevel === true) {\n                localisations = data.localisation;\n            }\n            else {\n                localisations = (data.localisation[0].hasOwnProperty('sublocalisations') === true && data.localisation[0].sublocalisations !== null && data.localisation[0].sublocalisations.hasOwnProperty('localisation')) ? data.localisation[0].sublocalisations.localisation : data.localisation;\n            }\n            var entry = null;\n            var localisation = null;\n            var label = '';\n            var thumb = '';\n            if (localisations !== null && typeof localisations !== \"undefined\" && localisations.hasOwnProperty('length')) {\n                for (var i = 0; i < localisations.length; i++) {\n                    localisation = localisations[i];\n                    label = localisation.hasOwnProperty('label') ? localisation.label : '';\n                    thumb = localisation.hasOwnProperty('thumb') ? localisation.thumb : null;\n\n                    if (localisation.hasOwnProperty('tcin') === true && localisation.hasOwnProperty('tcout') === true) {\n                        entry = {\n                            id: (localisation.hasOwnProperty('id') === true) ? localisation.id : null,\n                            shape: (localisation.hasOwnProperty('shape') === true) ? localisation.shape : null,\n                            data: (localisation.hasOwnProperty('data') === true) ? localisation.data : null,\n                            sublocalisations: (localisation.hasOwnProperty('sublocalisations') === true) ? localisation.sublocalisations : null,\n                            label: label,\n                            thumb: thumb,\n                            tclevel: (localisation.hasOwnProperty('tclevel') === true) ? localisation.tclevel : 0,\n                            tc: (typeof localisation.tc === \"number\") ? localisation.tc : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tc),\n                            tcin: (typeof localisation.tcin === \"number\") ? localisation.tcin : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tcin),\n                            tcout: (typeof localisation.tcout === \"number\") ? localisation.tcout : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tcout),\n                            duration: fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tcout) - fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tcin),\n                            type: (localisation.hasOwnProperty('type') === true) ? localisation.type : null,\n                            score: (localisation.hasOwnProperty('score') === true) ? parseFloat(localisation.score) : null\n                        };\n                        parsedMetadata.list.push(entry);\n                    }\n                    else if (localisation.hasOwnProperty('tc') === true && localisation.tc !== null) {\n                        entry = {\n                            id: (localisation.hasOwnProperty('id') === true) ? localisation.id : null,\n                            shape: (localisation.hasOwnProperty('shape') === true) ? localisation.shape : null,\n                            data: (localisation.hasOwnProperty('data') === true) ? localisation.data : null,\n                            sublocalisations: (localisation.hasOwnProperty('sublocalisations') === true) ? localisation.sublocalisations : null,\n                            label: label,\n                            thumb: thumb,\n                            tclevel: (localisation.hasOwnProperty('tclevel') === true) ? localisation.tclevel : 0,\n                            tc: (typeof localisation.tc === \"number\") ? localisation.tc : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tc),\n                            type: (localisation.hasOwnProperty('type') === true) ? localisation.type : null,\n                            score: (localisation.hasOwnProperty('score') === true) ? parseFloat(localisation.score) : null\n                        };\n                        parsedMetadata.list.push(entry);\n                    }\n                }\n            }\n        }\n        return parsedMetadata;\n    }\n});\n"
  },
  {
    "path": "src/player/player-html5.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * In charge of the player html5\r\n * @class PlayerHtml5\r\n * @module player\r\n * @namespace fr.ina.amalia.player\r\n * @constructor\r\n * @param {Object} settings\r\n * @param {Object} mediaContainer\r\n */\r\nfr.ina.amalia.player.BasePlayer.extend(\"fr.ina.amalia.player.PlayerHtml5\", {},\r\n    {\r\n\r\n\r\n        /**\r\n         * Method in charge to initialize player : - Create containers - Load\r\n         * plugins\r\n         * @method initialize\r\n         * @override\r\n         */\r\n        initialize: function () {\r\n            this.lastSeekTime = 0;\r\n            this.createPlayer();\r\n            this.setSrc(this.settings.src, this.settings.autoplay);\r\n            // set default volume\r\n            this.setVolume((this.localStorageManager.hasItem('volume') === false) ? this.settings.defaultVolume : this.localStorageManager.getItem('volume'));\r\n            this._super();\r\n        },\r\n        /**\r\n         * In charge to set source with autoplay state\r\n         * @param {String} src\r\n         * @param {Boolean} autoplay\r\n         * @method setSrc\r\n         */\r\n        setSrc: function (src, autoplay) {\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"setSrc:\" + src);\r\n            }\r\n\r\n            if (this.settings.mediaType === \"mpd\") {\r\n                this.mediaTypeManager = new fr.ina.amalia.player.media.type.DashMpeg(this.settings, this);\r\n                this.mediaTypeManager.setSrc(src);\r\n            }\r\n            else {\r\n                // charge la video\r\n                this.mediaPlayer.find('source:first').attr({\r\n                    src: src\r\n                });\r\n                if (this.settings.mediaType !== \"\") {\r\n                    this.mediaPlayer.find('source:first').attr('type', this.settings.mediaType);\r\n                }\r\n                this.load();\r\n            }\r\n            this._super(src, autoplay);\r\n        },\r\n        /**\r\n         * In charge to create Player dom element\r\n         * @method createPlayer\r\n         */\r\n        createPlayer: function () {\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"createPlayer\");\r\n            }\r\n\r\n            this.mediaPlayer = $('<video/>', {\r\n                'class': this.Class.mediaPlayerClassCss,\r\n                'style': this.Class.mediaPlayerStyleCss,\r\n                'x-webkit-airplay': 'allow'\r\n                //'webkit-playsinline':''\r\n            });\r\n            // Add cross origin\r\n            if (this.settings.crossorigin !== \"\") {\r\n                this.mediaPlayer.attr('crossorigin', this.settings.crossorigin);\r\n            }\r\n            // preload\r\n            if (this.settings.hasOwnProperty('preload') && this.settings.preload !== \"\") {\r\n                this.mediaPlayer.attr('preload', this.settings.preload);\r\n            }\r\n\r\n            if (this.getPoster() !== \"\") {\r\n                this.mediaPlayer.attr('poster', this.getPoster());\r\n            }\r\n\r\n\r\n            //support mpeg dash\r\n            if (this.settings.mediaType !== \"mpd\") {\r\n                var source = $('<source />');\r\n                this.mediaPlayer.append(source);\r\n            }\r\n            this.mediaContainer.append(this.mediaPlayer);\r\n            this.initEvents();\r\n        },\r\n\r\n        /**\r\n         * In charge to set player events\r\n         * @method initEvents\r\n         */\r\n        initEvents: function () {\r\n            this._super();\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"adEvents\");\r\n            }\r\n            this.mediaPlayer.on('loadstart', {\r\n                self: this\r\n            }, this.onLoadstart);\r\n            this.mediaPlayer.on('playing', {\r\n                self: this\r\n            }, this.onPlay);\r\n            this.mediaPlayer.on('pause', {\r\n                self: this\r\n            }, this.onPause);\r\n            this.mediaPlayer.on('ended', {\r\n                self: this\r\n            }, this.onEnded);\r\n            this.mediaPlayer.one('durationchange', {\r\n                self: this\r\n            }, this.onDurationchange);\r\n            this.mediaPlayer.on('timeupdate', {\r\n                self: this\r\n            }, this.onTimeupdate);\r\n            this.mediaPlayer.find(\"source\").on('error', {\r\n                self: this\r\n            }, this.onSourceError);\r\n            this.mediaPlayer.on('seeked', {\r\n                self: this\r\n            }, this.onSeeked);\r\n            if (this.settings.togglePlayPause === true) {\r\n                this.mediaPlayer.on('click', $.proxy(this.togglePlayPause, this));\r\n            }\r\n            $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenChange MSFullscreenChange', {\r\n                self: this\r\n            }, this.onFullscreenHandler);\r\n\r\n        },\r\n        /**\r\n         * In charge to load media\r\n         * @method load\r\n         */\r\n        load: function () {\r\n            this.mediaPlayer.get(0).load();\r\n        },\r\n\r\n        /**\r\n         * In charge to play media\r\n         * @method play\r\n         */\r\n        play: function () {\r\n            this.mediaPlayer.get(0).play();\r\n            this._super();\r\n        },\r\n        /**\r\n         * In charge to pause media\r\n         * @method pause\r\n         */\r\n        pause: function () {\r\n            this._super();\r\n            this.mediaPlayer.get(0).pause();\r\n\r\n        },\r\n        /**\r\n         * In charge to stop media\r\n         * @method stop\r\n         */\r\n        stop: function () {\r\n            this.mediaPlayer.get(0).pause();\r\n            this.mediaPlayer.get(0).currentTime = 0;\r\n            this._super();\r\n        },\r\n\r\n        /**\r\n         * In charge to set mute state\r\n         * @method mute\r\n         * @event fr.ina.amalia.player.PlayerEventType.MUTE\r\n         * @return {Number}\r\n         */\r\n        mute: function () {\r\n            this.mediaPlayer.get(0).volume = 0;\r\n            this._super();\r\n        },\r\n        /**\r\n         * In charge to set unmute state\r\n         * @method unmute\r\n         * @event fr.ina.amalia.player.PlayerEventType.UN_MUTE\r\n         */\r\n        unmute: function () {\r\n            this.mediaPlayer.get(0).volume = 1;\r\n            this._super();\r\n        },\r\n\r\n        /**\r\n         * Return media duration with tc offset\r\n         * @method getDuration\r\n         * @return {Number}\r\n         */\r\n        getDuration: function () {\r\n            return this._super(this.settings.duration === null ? this.mediaPlayer.get(0).duration : parseFloat(this.settings.duration));\r\n        },\r\n\r\n        /**\r\n         * Returns the player's current volume, an integer between 0 and 100. Note that getVolume() will return the volume even if the player is muted.\r\n         * @method getVolume\r\n         * @return {Number}\r\n         */\r\n        getVolume: function () {\r\n            return this.mediaPlayer.get(0).volume * 100;\r\n        },\r\n\r\n        /**\r\n         * Sets the volume. Accepts an integer between 0 and 100.\r\n         * @method setVolume\r\n         * @param {Number} volume\r\n         */\r\n        setVolume: function (volume) {\r\n            this.mediaPlayer.get(0).volume = volume / 100;\r\n            return this._super(volume);\r\n        },\r\n        /**\r\n         * Return current position in seconds\r\n         * @method getCurrentTime\r\n         * @return {Number}\r\n         */\r\n        getCurrentTime: function () {\r\n            return this._super(this.mediaPlayer.get(0).currentTime);\r\n        },\r\n        /**\r\n         * Set seek position in seconds\r\n         * @method setCurrentTime\r\n         * @param {Object} value\r\n         * @event fr.ina.amalia.player.PlayerEventType.SEEK\r\n         * @return return current time without tc offset.\r\n         */\r\n        setCurrentTime: function (value) {\r\n            var currentTime = isNaN(value) ? 0 : value;\r\n            if (this.settings.duration === null) {\r\n                this.mediaPlayer.get(0).currentTime = Math.max(0, currentTime - this.tcOffset);\r\n            } else {\r\n                this.lastSeekTime = Math.max(0, currentTime - this.tcOffset);\r\n                var startTime = Math.max(0, currentTime - this.tcOffset);\r\n                var newSrc = this.settings.src.search(/\\?/) === -1 ? this.settings.src + '?start=' + startTime : this.settings.src + '&start=' + startTime;\r\n                this.setSrc(newSrc, true);\r\n            }\r\n            this._super(currentTime);\r\n        },\r\n        /**\r\n         * Return playback rate\r\n         * @returns the current playback speed of the audio/video.\r\n         */\r\n        getPlaybackrate: function () {\r\n            return this.mediaPlayer.get(0).playbackRate;\r\n        },\r\n        /**\r\n         * Set playback rate\r\n         * @param {Objecy} speed the current playback speed of the audio/video.\r\n         * @returns the current playback speed of the audio/video.\r\n         */\r\n        setPlaybackrate: function (speed) {\r\n            var self = this;\r\n            if (speed <= 0) {\r\n                clearInterval(self.intervalRewind);\r\n                self.intervalRewind = setInterval(function () {\r\n                    self.mediaPlayer.get(0).playbackRate = 1;\r\n                    var currentTime = self.getCurrentTime();\r\n                    if (currentTime === 0) {\r\n                        self.mediaPlayer.get(0).playbackRate = 1.0;\r\n                        clearInterval(self.intervalRewind);\r\n                        self.pause();\r\n                    }\r\n                    else {\r\n                        currentTime += speed;\r\n                        self.setCurrentTime(currentTime);\r\n                    }\r\n                }, 30);\r\n            }\r\n            else {\r\n                clearInterval(self.intervalRewind);\r\n                if (this.isPaused()) {\r\n                    this.play();\r\n                }\r\n                self.mediaPlayer.get(0).playbackRate = parseFloat(speed);\r\n            }\r\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PLAYBACK_RATE_CHANGE, {\r\n                rate: speed\r\n            });\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"setPlaybackrate :\" + speed);\r\n            }\r\n        },\r\n        /**\r\n         * return media content type\r\n         * @method getContentType\r\n         * @return {Boolean} description\r\n         */\r\n        getContentType: function () {\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"getContentType\");\r\n            }\r\n            return this.mediaPlayer.find('source').get(0);\r\n        },\r\n        /**\r\n         * Returns the current fullscreen state\r\n         * @method getFullscreenState\r\n         * @return {Boolean} true  if container is in full-screen mode\r\n         */\r\n        getFullscreenState: function () {\r\n            return fr.ina.amalia.player.helpers.HTML5Helper.inFullScreen();\r\n        },\r\n\r\n        /**\r\n         * Return true if media is paused\r\n         * @method isPaused\r\n         * @return {Boolean}\r\n         */\r\n        isPaused: function () {\r\n            return this.mediaPlayer.get(0).paused;\r\n        },\r\n\r\n        /**\r\n         * Fired on full-screen state change\r\n         * @method onFullscreenHandler\r\n         * @event fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE\r\n         * @param {Object} event\r\n         */\r\n        onFullscreenHandler: function (event) {\r\n            var inFullScreen = fr.ina.amalia.player.helpers.HTML5Helper.inFullScreen();\r\n            var targetElement = (typeof event.originalEvent.originalTarget === \"object\" && typeof event.originalEvent.originalTarget.mozFullScreenElement === \"object\") ? $(event.originalEvent.originalTarget.mozFullScreenElement) : $(event.originalEvent.target);\r\n            if (targetElement.attr('id') === event.data.self.mediaContainer.attr('id') || inFullScreen === false) {\r\n                event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE, {\r\n                    inFullScreen: inFullScreen\r\n                });\r\n            }\r\n            else if (event.type === \"MSFullscreenChange\") {\r\n                if ($(document.msFullscreenElement).attr('id') === event.data.self.mediaContainer.attr('id') || inFullScreen === false) {\r\n                    event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE, {\r\n                        inFullScreen: inFullScreen\r\n                    });\r\n                }\r\n            }\r\n            event.data.self.mediaContainer.toggleClass('ajs-fullscreen', inFullScreen);\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onFullscreenHandler :\" + inFullScreen);\r\n            }\r\n        },\r\n\r\n        /**\r\n         * In charge to set zoom tc\r\n         * @param {Number} zTcin\r\n         * @param {Number} zTcout\r\n         * @param {String} eventTag\r\n         */\r\n        setZoomTc: function (zTcin, zTcout, eventTag) {\r\n            eventTag = (typeof eventTag !== 'undefined') ? eventTag : '';\r\n            if (Math.ceil(this.zTcin) !== Math.ceil(zTcin) || Math.ceil(this.zTcout) !== Math.ceil(zTcout)) {\r\n                this.zTcin = Math.max(0, parseFloat(zTcin));\r\n                this.zTcout = parseFloat(zTcout);\r\n                if (this.logger !== null) {\r\n                    this.logger.info(\"SetZoomTc: // Tcin: \" + this.zTcin + \" // Tcout:\" + this.zTcout + \" // Event Tag Name:\" + eventTag);\r\n                }\r\n                this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.ZOOM_RANGE_CHANGE, {\r\n                    zTcin: parseFloat(zTcin),\r\n                    zTcout: parseFloat(zTcout),\r\n                    eventTag: eventTag\r\n                });\r\n            }\r\n        },\r\n        /**\r\n         * Return current image\r\n         * @param {Nomber} scale max 1=> 100%\r\n         * @method getCurrentImage\r\n         * @event fr.ina.amalia.player.PlayerEventType.IMAGE_CAPTURE\r\n         */\r\n        getCurrentImage: function (scale) {\r\n            var image = \"\";\r\n            try {\r\n                var videoContent = this.mediaPlayer.get(0);\r\n                var canvas = document.createElement(\"canvas\");\r\n                scale = (typeof scale !== 'undefined') ? Math.min(1, parseFloat(scale)) : 1;\r\n                canvas.width = videoContent.videoWidth * scale;\r\n                canvas.height = videoContent.videoHeight * scale;\r\n                canvas.getContext('2d').drawImage(videoContent, 0, 0, canvas.width, canvas.height);\r\n                image = canvas.toDataURL(\"image/png\");\r\n                this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.IMAGE_CAPTURE, {\r\n                    currentTime: this.getCurrentTime(),\r\n                    captureTc: this.getCurrentTime(),\r\n                    captureId: this.getCurrentTime(),\r\n                    captureImage: image.toString()\r\n                });\r\n                return image;\r\n            }\r\n            catch (error) {\r\n                if (this.logger !== null) {\r\n                    this.logger.warn(\"Error lors de la capture d'imagette\");\r\n                    this.logger.warn(error.stack);\r\n                }\r\n            }\r\n            return image;\r\n        },\r\n        /**\r\n         * Return current image for specified time code.\r\n         * @param {String} id\r\n         * @param {Number} tc\r\n         * @param {Number} scale max 1=> 100%\r\n         * @method getTcImage\r\n         * @event fr.ina.amalia.player.PlayerEventType.IMAGE_CAPTURE\r\n         */\r\n        getTcImage: function (id, tc, scale) {\r\n            this.setCurrentTime(tc);\r\n            // Need to call player for make capture\r\n            this.play();\r\n            this.captureId = id;\r\n            this.captureTc = tc + this.tcOffset;\r\n            this.captureScale = (typeof scale !== 'undefined') ? Math.min(1, parseFloat(scale)) : 1;\r\n        },\r\n        /**\r\n         * In charge to move next frame\r\n         * @method moveNextFrame\r\n         * @param {Object} event\r\n         */\r\n        moveNextFrame: function () {\r\n            this.pause();\r\n            this.setCurrentTime(Math.min(this.getDuration() + this.tcOffset, this.getCurrentTime() + (1 / this.settings.framerate)));\r\n        },\r\n        /**\r\n         * In charge to move prev frame\r\n         * @method movePrevFrame\r\n         * @param {Object} event\r\n         */\r\n        movePrevFrame: function () {\r\n            this.pause();\r\n            this.setCurrentTime(Math.max(0, this.getCurrentTime() - (1 / this.settings.framerate)));\r\n        },\r\n        /**\r\n         * Fired on load start event\r\n         * @method onLoadstart\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.STARTED\r\n         */\r\n        onLoadstart: function (event) {\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onLoadstart\");\r\n            }\r\n            if (typeof event.data.self.settings.callbacks.onReady !== \"undefined\") {\r\n                try {\r\n                    /* jslint evil: true */\r\n                    eval(event.data.self.settings.callbacks.onReady + '()');\r\n                }\r\n                catch (e) {\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.warn(\"Send callback failed.\");\r\n                    }\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Fired on playing event\r\n         * @method onPlay\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.PLAYING\r\n         */\r\n        onPlay: function (event) {\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPlay\");\r\n            }\r\n            event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PLAYING, [\r\n                event.data.self\r\n            ]);\r\n        },\r\n        /**\r\n         * Fired on paused event\r\n         * @method onPause\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.PAUSED\r\n         */\r\n        onPause: function (event) {\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPause\");\r\n            }\r\n            event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PAUSED, [\r\n                event.data.self\r\n            ]);\r\n        },\r\n        /**\r\n         * @method onSeeked\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.SEEK\r\n         */\r\n        onSeeked: function (event) {\r\n            event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.SEEK, {\r\n                currentTime: event.data.self.getCurrentTime()\r\n            });\r\n            // Lecture d'un segment\r\n            if (typeof event.data.self.captureTc === \"number\") {\r\n                try {\r\n                    event.data.self.getCurrentImage(event.data.self.captureScale);\r\n                    event.data.self.captureTc = null;\r\n                }\r\n                catch (error) {\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.warn(\"Error to capture Tc\");\r\n                        event.data.self.logger.warn(error.stack);\r\n                    }\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Ended event occurs when the audio/video has reached the end.\r\n         * @method onEnded\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.ENDED\r\n         */\r\n        onEnded: function (event) {\r\n\r\n            if (event.data.self.settings.duration !== null) {\r\n                event.data.self.setCurrentTime(event.data.self.tcOffset);\r\n                event.data.self.stop();\r\n            }\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onEnded\");\r\n            }\r\n            event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.ENDED, [\r\n                event.data.self\r\n            ]);\r\n            if (typeof event.data.self.settings.callbacks.onComplete !== \"undefined\") {\r\n                try {\r\n                    /* jslint evil: true */\r\n                    eval(event.data.self.settings.callbacks.onComplete + '()');\r\n                }\r\n                catch (e) {\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.warn(\"Send callback failed.\");\r\n                    }\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Fired on media duration change\r\n         * @method onFirstTimeUpdate\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.STARTED\r\n         */\r\n        onDurationchange: function (event) {\r\n            event.data.self.hideLoader();\r\n            if (!isNaN(event.data.self.getDuration()) && event.data.self.getDuration() !== 0) {\r\n                event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.STARTED, [\r\n                    event.data.self\r\n                ]);\r\n                if (event.data.self.settings.autoplay === true) {\r\n                    event.data.self.play();\r\n                }\r\n            }\r\n            else {\r\n                event.data.self.mediaPlayer.one('durationchange', {\r\n                    self: event.data.self\r\n                }, event.data.self.onDurationchange);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on time change evnet\r\n         * @method onTimeupdate\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.TIME_CHANGE\r\n         */\r\n        onTimeupdate: function (event) {\r\n            var tcOffset = event.data.self.getTcOffset();\r\n            var currentTime = event.data.self.getCurrentTime();\r\n            var duration = event.data.self.getDuration() + tcOffset;\r\n            var percentage = ((currentTime - tcOffset) * 100) / (duration - tcOffset);\r\n\r\n            event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\r\n                self: event.data.self,\r\n                currentTime: currentTime,\r\n                duration: duration,\r\n                percentage: percentage,\r\n                tcOffset: event.data.self.getTcOffset()\r\n            });\r\n            // In Range play mode\r\n            if (event.data.self.isRangePlayer === true && typeof event.data.self.rangePlayerTcout === \"number\" && event.data.self.rangePlayerTcout <= currentTime) {\r\n                event.data.self.pause();\r\n            }\r\n            if (typeof event.data.self.settings.callbacks.onTimeupdate !== \"undefined\") {\r\n                try {\r\n                    /* jslint evil: true */\r\n                    eval(event.data.self.settings.callbacks.onTimeupdate + '(currentTime)');\r\n                }\r\n                catch (e) {\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.warn(\"Send callback failed.\");\r\n                    }\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Fired when error to load media\r\n         * @method onSourceError\r\n         * @param {Object} event\r\n         * @event fr.ina.amalia.player.PlayerEventType.ERROR\r\n         */\r\n        onSourceError: function (event) {\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.warn(event.data.self.Class.fullName + \" :: onSourceError\");\r\n                event.data.self.logger.warn(event);\r\n            }\r\n            event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.ERROR, {\r\n                self: event.data.self,\r\n                errorCode: fr.ina.amalia.player.PlayerErrorCode.MEDIA_FILE_NOT_FOUND\r\n            });\r\n        }\r\n\r\n    });\r\n"
  },
  {
    "path": "src/player/plugins/captions-base.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class for caption plugins\n * @class CaptionsBase\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-captions\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.captions.CaptionsBase\", {}, {\n    /**\n     * Dom element\n     * @property container\n     * @type {Object}\n     * @default null\n     */\n    container: null,\n    /**\n     * Dom element\n     * @property container\n     * @type {Array}\n     * @default []\n     */\n    listOfData: [],\n    /**\n     * Set player events listner\n     * @method definePlayerListeners\n     */\n    definePlayerListeners: function () {\n        var container = this.mediaPlayer.getContainer();\n        // Player events\n        container.on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n            self: this\n        }, this.onTimeupdate);\n\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n        }\n    },\n    /**\n     * Update metadata\n     * @param {String} metadataId\n     * @param {Number} level\n     * @param {Number} tcin\n     * @param {Number} tcout\n     * @param {Boolean} withMainLevel\n     */\n    updateMetadata: function (metadataId, level, tcin, tcout, withMainLevel) {\n        //Clear old data\n        this.listOfData = [];\n        var localisations = null;\n        var listOfObject = null;\n        withMainLevel = (withMainLevel === true);\n\n        listOfObject = this.mediaPlayer.getMetadataWithRange(metadataId, tcin, tcout);\n\n        for (var i = 0;\n             i < listOfObject.length;\n             i++) {\n            localisations = listOfObject[i];\n            if (localisations.hasOwnProperty('sublocalisations') === true && localisations.sublocalisations !== null) {\n                this._parseSubLocalisations(localisations.sublocalisations, level, withMainLevel);\n            }\n            else if (localisations !== null) {\n                this._parseLocalisation(localisations, level, withMainLevel);\n            }\n            else {\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"Error to set data, index: \" + i);\n                }\n            }\n        }\n    },\n    /**\n     * Parse localisations blocks\n     * @method _parseLocalisation\n     * @param {Object} localisation\n     * @param {Number} level\n     * @param withMainLevel\n     */\n    _parseLocalisation: function (localisation, level, withMainLevel) {\n        var data = null;\n        if (localisation.tclevel <= level) {\n            if ((localisation.tclevel === level && withMainLevel === false) || (localisation.tclevel <= level && withMainLevel === true)) {\n                var text = this._getText(localisation.data);\n                if (text !== '') {\n                    data = {\n                        'tcin': (typeof localisation.tcin === \"number\") ? localisation.tcin : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tcin),\n                        'tcout': (typeof localisation.tcout === \"number\") ? localisation.tcout : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(localisation.tcout),\n                        'text': text,\n                        'label': localisation.label,\n                        'thumb': localisation.thumb,\n                        'level': localisation.tclevel\n                    };\n                    this.listOfData.push(data);\n                }\n            }\n            if (localisation.hasOwnProperty('sublocalisations') === true && localisation.sublocalisations !== null && localisation.sublocalisations.hasOwnProperty('localisation') === true && $.isArray(localisation.sublocalisations.localisation)) {\n                this._parseSubLocalisations(localisation.sublocalisations, level, withMainLevel);\n            }\n        }\n    },\n    /**\n     * Parse sub localisations blocks\n     * @method _parseSubLocalisations\n     * @param {Object} sublocalisations\n     * @param {Object} level\n     * @param {Boolean} withMainLevel\n     * @returns {sublocalisations.localisation}\n     */\n    _parseSubLocalisations: function (sublocalisations, level, withMainLevel) {\n        var localisation = null;\n        if (sublocalisations !== null && typeof sublocalisations !== 'undefined' && sublocalisations.hasOwnProperty('localisation') === true && $.isArray(sublocalisations.localisation)) {\n            for (var i = 0;\n                 i < sublocalisations.localisation.length;\n                 i++) {\n                localisation = sublocalisations.localisation[i];\n                this._parseLocalisation(localisation, level, withMainLevel);\n            }\n        }\n        return localisation;\n    },\n    /**\n     * Return text with data.text block\n     * @method _getText\n     * @param {Object} data\n     * @returns {String}\n     */\n    _getText: function (data) {\n        var text = '';\n        if (data !== null && typeof data !== 'undefined' && data.hasOwnProperty('text') === true && data.text !== null && $.isArray(data.text)) {\n            for (var i = 0;\n                 i < data.text.length;\n                 i++) {\n                if (data.text[i] !== null) {\n                    text += data.text[i].toString();\n                }\n            }\n        }\n        return text;\n    },\n    /**\n     * Update current time pos\n     * @method updatePos\n     * @param {Object} currentTime\n     */\n    updatePos: function (currentTime) {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"updatePos : \" + currentTime);\n        }\n    },\n    // /**Player events**/\n    /**\n     * Fired when time change event\n     * @method onTimeupdate\n     * @param {Object} event\n     * @param {Object} data\n     */\n    onTimeupdate: function (event, data) {\n        event.data.self.updatePos(parseFloat(data.currentTime));\n    }\n});\n"
  },
  {
    "path": "src/player/plugins/context-menu-plugin.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Context menu plugin\n * @class ContextMenuPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-context-menu\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.ContextMenuPlugin\", {\n        classCss: \"plugin-context-menu dropdown-menu lg\",\n        style: \"\",\n        copyrightText: _PlayerAmaliaVersion_,\n        copyrightHomepage: _PlayerAmaliaHomepage_\n    },\n    {\n        /**\n         * Dom element property\n         * @property container\n         * @type {Object}\n         * @default null\n         */\n        container: null,\n        /**\n         * Context menu configuration\n         * @property menuConfiguration\n         * @type {Object}\n         * @default {}\n         */\n        menuConfiguration: {\n            \"position\": {\n                my: \"left top\",\n                at: \"right top\"\n            }\n        },\n        /**\n         * Initialize the component\n         * @method initialize\n         */\n        initialize: function () {\n            this.initContextMenu();\n        },\n        /**\n         * In charge to create context menu\n         * @method initContextMenu\n         */\n        initContextMenu: function () {\n            this.container = $('<ul/>', {\n                'class': this.Class.classCss,\n                'style': this.Class.style,\n                'role': 'menu',\n                'aria-labelledby': 'dLabel'\n            }).menu({\n                disabled: false\n            }).css({\n                position: \"fixed\",\n                left: 0,\n                top: 0\n            }).hide();\n            this.addItemWithLink(this.Class.copyrightText, null, null, 'ajs-icon  ajs-icon-amalia-js');\n            this.addItemWithLink(fr.ina.amalia.player.PlayerMessage.PLAYER_OPENSOURCE_LINK, this.Class.copyrightHomepage, 'homepage', 'ajs-icon ajs-icon-github');\n            this.pluginContainer.append(this.container);\n            // events\n            this.container.on('mouseleave', {\n                self: this\n            }, this.onMouseleave);\n\n            this.pluginContainer.parent().on('contextmenu', {\n                self: this\n            }, this.onContextmenu);\n        },\n        /**\n         * Add menu item with link\n         * @param {String} text\n         * @param {String} link\n         * @param {String} className\n         * @returns {Object}\n         */\n        addItemWithLink: function (text, link, className, icon) {\n            link = (link) ? link : '#';\n            icon = (typeof icon === 'string') ? icon : null;\n            className = (className) ? className : 'disabled';\n            var item = $('<li>', {\n                'class': className\n            });\n            var linkElement = $('<a>', {\n                text: text,\n                target: '_blank',\n                href: link\n            });\n\n            ///Add icon\n            if (icon !== null) {\n                linkElement.append($('<span>', {\n                    'class': icon\n                }));\n            }\n            //disabled\n            if (link === '#') {\n                linkElement.on('click', function (e) {\n                    e.preventDefault();\n                });\n            }\n            item.append(linkElement);\n            this.container.append(item);\n            return item;\n        },\n        /**\n         * Fired on context menu event\n         * @method onContextmenu\n         * @param {Object} e\n         */\n        onContextmenu: function (e) {\n            var offset = 10;\n\n            e.data.self.container.show().css({\n                left: e.clientX - offset,\n                top: e.clientY - offset\n            });\n            e.preventDefault();\n        },\n        /**\n         * Fired on mouse leave event\n         * @method onMouseleave\n         * @param {Object} e\n         */\n        onMouseleave: function (e) {\n            e.preventDefault();\n            e.data.self.container.hide();\n        }\n    });\n"
  },
  {
    "path": "src/player/plugins/custom-control-bar.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * In charge of custom control bar plugin and manage widgets\r\n * @class CustomControlBarPlugin\r\n * @namespace fr.ina.amalia.player.plugins\r\n * @module plugin\r\n * @submodule plugin-controlbar\r\n * @constructor\r\n * @extends fr.ina.amalia.player.plugins.PluginBase\r\n * @param {Object} settings\r\n * @param {Object} pluginContainer\r\n */\r\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.CustomControlBarPlugin\", {\r\n        classCss: \"plugin-custom-controlbar\"\r\n    },\r\n    {\r\n        /**\r\n         * Plugin container object\r\n         * @property container\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        container: null,\r\n        /**\r\n         * left container element\r\n         * @property leftContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        leftContainer: null,\r\n        /**\r\n         * Middle container element\r\n         * @property midContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        midContainer: null,\r\n        /**\r\n         * Right container element\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        rightContainer: null,\r\n        /**\r\n         * List of widgets\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        widgets: null,\r\n        /**\r\n         * Control bar hight\r\n         * @property height\r\n         * @type {Object}\r\n         * @default 130\r\n         */\r\n        height: 130,\r\n        /**\r\n         * Progress bar dom element\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        progressBar: null,\r\n        /**\r\n         * Auto hide state, true if auto hide is enabled.\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        autoHide: true,\r\n        /**\r\n         * Control bar display state, false when the control bar is hide.\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        displayState: true,\r\n        /**\r\n         * Hide duration\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        hideDuration: 500,\r\n        /**\r\n         * Auto hide state, true when the auto hide state is started.\r\n         * @property rightContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        autoHideStarted: false,\r\n        /**\r\n         * Time Indicator Container\r\n         * @property timeIndicator\r\n         * @type {Number}\r\n         * @default 0\r\n         */\r\n        timeIndicatorContainer: null,\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            this.settings = $.extend({\r\n                    height: '80',\r\n                    autohide: true,\r\n                    hideDuration: 500,\r\n                    timeFormat: 'ms',\r\n                    framerate: 25,\r\n                    framepreview: false,\r\n                    framepreviewTimeBound: 500,\r\n                    sticky: false,\r\n                    widgets: {\r\n                        left: {\r\n                            'timelabelWidget': 'fr.ina.amalia.player.plugins.controlBar.widgets.TimeLabel'\r\n                        },\r\n                        mid: {\r\n                            'playWidget': 'fr.ina.amalia.player.plugins.controlBar.widgets.PlayButton',\r\n                            'pauseWidget': 'fr.ina.amalia.player.plugins.controlBar.widgets.PauseButton'\r\n                            //'jogShuttleButton':'fr.ina.amalia.player.plugins.controlBar.widgets.JogShuttleButton'\r\n                        },\r\n                        right: {\r\n                            'full': 'fr.ina.amalia.player.plugins.controlBar.widgets.FullscreenButton',\r\n                            //'volume': 'fr.ina.amalia.player.plugins.controlBar.widgets.ChannelVolumeControlBar'\r\n                            'volume': 'fr.ina.amalia.player.plugins.controlBar.widgets.VolumeControlBar'\r\n                        },\r\n                        settings: {}\r\n                    }\r\n                },\r\n                this.settings || {});\r\n\r\n            this.timeIndicatorContainer = null;\r\n            this.leftContainer = null;\r\n            this.midContainer = null;\r\n            this.rightContainer = null;\r\n            this.widgets = [];\r\n            this.autoHide = (this.settings.sticky === true) ? false : this.settings.autohide;\r\n            this.displayState = true;\r\n            this.hideDuration = this.settings.hideDuration;\r\n            this.height = this.settings.height;\r\n            this.autoHideStarted = false;\r\n            this.createControlBar();\r\n            this.initializeWidgets();\r\n            // add events\r\n            this.definePlayerListeners();\r\n            this.updatePlayerSize(false);\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"initialize\");\r\n                this.logger.info(this.settings);\r\n            }\r\n        },\r\n        /**\r\n         * Create control bar elements.\r\n         * @method createControlBar\r\n         */\r\n        createControlBar: function () {\r\n            var classCss = (this.settings.sticky === true) ? this.Class.classCss + ' sticky' : this.Class.classCss;\r\n            this.container = $('<div>', {\r\n                'class': classCss\r\n            });\r\n\r\n            this.timeIndicatorContainer = $('<div>', {\r\n                class: 'ajs-time-indicator'\r\n            });\r\n            var timeIndicator = $('<span>', {\r\n                class: 'ajs-tooltip-text'\r\n            });\r\n            this.timeIndicatorContainer.append(timeIndicator);\r\n            this.container.append(this.timeIndicatorContainer);\r\n            var row = $('<div>', {\r\n                'class': 'ajs-row'\r\n            });\r\n            this.leftContainer = $('<div>', {\r\n                'class': \"ajs-container left-container\"\r\n            });\r\n            this.midContainer = $('<div>', {\r\n                'class': \"ajs-container middle-container\"\r\n            });\r\n            this.rightContainer = $('<div>', {\r\n                'class': \"ajs-container right-container\"\r\n            });\r\n            // Add to main container\r\n            row.append(this.leftContainer);\r\n            row.append(this.midContainer);\r\n            row.append(this.rightContainer);\r\n            this.container.append(row);\r\n            this.container.height(this.height);\r\n            this.pluginContainer.append(this.container);\r\n        },\r\n        /**\r\n         * Initialize widgets\r\n         * @method initializeWidgets\r\n         */\r\n        initializeWidgets: function () {\r\n            var widgets = this.settings.widgets;\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"initialize\");\r\n                this.logger.info(widgets);\r\n            }\r\n            if (widgets.hasOwnProperty('left')) {\r\n                for (var leftWidgetName in widgets.left) {\r\n                    if (widgets.left.hasOwnProperty(leftWidgetName)) {\r\n                        this.initWidget(leftWidgetName, widgets.left[leftWidgetName], this.leftContainer);\r\n                    }\r\n                }\r\n            }\r\n            if (widgets.hasOwnProperty('mid')) {\r\n                for (var midWidgetName in widgets.mid) {\r\n                    if (widgets.mid.hasOwnProperty(midWidgetName)) {\r\n                        this.initWidget(midWidgetName, widgets.mid[midWidgetName], this.midContainer);\r\n                    }\r\n                }\r\n            }\r\n            if (widgets.hasOwnProperty('right')) {\r\n                for (var rightWidgetName in widgets.right) {\r\n                    if (widgets.right.hasOwnProperty(rightWidgetName)) {\r\n                        this.initWidget(rightWidgetName, widgets.right[rightWidgetName], this.rightContainer);\r\n                    }\r\n                }\r\n            }\r\n\r\n            if (this.settings.enableProgressBar !== false) {\r\n                this.progressBar = new fr.ina.amalia.player.plugins.controlBar.widgets.ProgressBar({\r\n                    debug: this.settings.debug,\r\n                    framepreview: this.settings.framepreview,\r\n                    framepreviewTimeBound: this.settings.framepreviewTimeBound\r\n                }, this.mediaPlayer, this.container);\r\n            }\r\n\r\n            //Set events\r\n            this.container.find('.player-progress-bar').on('mouseover', $.proxy(this.onProgressBarMouseover, this));\r\n            this.container.find('.player-progress-bar').on('mouseout', $.proxy(this.onProgressBarMouseoout, this));\r\n            this.container.find('.player-progress-bar').on('mousemove', $.proxy(this.onProgressBarMousemove, this));\r\n\r\n        },\r\n        /**\r\n         * Initialize widget\r\n         * @method initWidget\r\n         * @param {Object} widgetName\r\n         * @param {Object} widgetClassName\r\n         * @param {Object} container\r\n         */\r\n        initWidget: function (widgetName, widgetClassName, container) {\r\n            try {\r\n                var settings = (this.settings.widgets.hasOwnProperty('settings') && typeof this.settings.widgets.settings[widgetName] === \"object\") ? this.settings.widgets.settings[widgetName] : this.settings;\r\n                /* jslint evil: true */\r\n                /* jshint unused:false */\r\n                var obj = eval('new ' + widgetClassName + '(settings,this.mediaPlayer,container)');\r\n                this.widgets.push(obj);\r\n            }\r\n            catch (error) {\r\n                if (this.logger !== null) {\r\n                    this.logger.warn(\"Error to load widget : \" + widgetClassName);\r\n                    this.logger.warn(error.stack);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Add player events listener\r\n         * @method definePlayerListeners\r\n         */\r\n        definePlayerListeners: function () {\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE, {\r\n                self: this\r\n            }, this.onFullscreenModeChange);\r\n            if (this.autoHide === true) {\r\n                this.mediaPlayer.getContainer().one(fr.ina.amalia.player.PlayerEventType.PLAYING, {\r\n                    self: this\r\n                }, this.onPlaying);\r\n                this.mediaPlayer.getContainer().one(fr.ina.amalia.player.PlayerEventType.SEEK, {\r\n                    self: this\r\n                }, this.onSeeking);\r\n            }\r\n            // call function 200 ms after resize is complete.\r\n            $(window).on('debouncedresize', {\r\n                self: this\r\n            }, this.onWindowResize);\r\n        },\r\n        /**\r\n         * Update control bar height\r\n         * @method updatePlayerSize\r\n         * @param {Boolean} inFullScreen\r\n         */\r\n        updatePlayerSize: function (inFullScreen) {\r\n            if (this.settings.sticky === true) {\r\n                if (inFullScreen === true) {\r\n                    this.mediaPlayer.getContainer().find('video').first().css('height', $(window).height()- this.height);\r\n                }\r\n                else {\r\n                    this.mediaPlayer.getContainer().find('video').first().css('height', parseInt(this.mediaPlayer.mediaContainer.parent().height() - this.height));\r\n                }\r\n                if (this.logger !== null) {\r\n                    this.logger.trace(this.Class.fullName, \"size : \" + this.mediaPlayer.getContainer().find('video').first().css('height'));\r\n                }\r\n            }\r\n            else if (this.autoHideStarted === false) {\r\n                this.container.css('bottom', this.height + 'px');\r\n            }\r\n            else {\r\n                this.container.css('bottom', this.displayState === true ? this.height : 10 + 'px');\r\n            }\r\n        },\r\n        /**\r\n         * Show control bar with animation\r\n         * @method show\r\n         */\r\n        show: function () {\r\n            var self = this;\r\n            if (self.displayState === false) {\r\n                self.container.find('.ajs-row').show();\r\n                self.container.animate({\r\n                    bottom: self.height + 'px'\r\n                }, 250, function () {\r\n                    self.displayState = true;\r\n                });\r\n            }\r\n        },\r\n        /**\r\n         * Hide control bar with animation\r\n         * @method hide\r\n         */\r\n        hide: function () {\r\n            var self = this;\r\n            if (self.displayState === true) {\r\n                self.container.animate({\r\n                    bottom: '10px'\r\n                }, 250, function () {\r\n                    self.container.find('.ajs-row').hide();\r\n                    self.displayState = false;\r\n                });\r\n            }\r\n        },\r\n        /**\r\n         * Fired when full-screen mode change\r\n         * @method onFullscreenModeChange\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onFullscreenModeChange: function (event, data) {\r\n            event.data.self.updatePlayerSize(data.inFullScreen);\r\n        },\r\n        /**\r\n         * Fired when mouse enter\r\n         * @mthod onPlayerMouseEnter\r\n         * @param {Object} event\r\n         */\r\n        onPlayerMouseEnter: function (event) {\r\n            if (event.data.self.displayState === false) {\r\n                event.data.self.show();\r\n            }\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPlayerMouseEnter\");\r\n            }\r\n        },\r\n        /**\r\n         * Fired when mouse leave event\r\n         * @method onPlayerMouseLeave\r\n         * @param {Object} event\r\n         */\r\n        onPlayerMouseLeave: function (event) {\r\n            if (event.data.self.displayState === true) {\r\n                setTimeout(function () {\r\n                    event.data.self.hide();\r\n                }, event.data.self.hideDuration);\r\n            }\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPlayerMouseLeave\");\r\n            }\r\n        },\r\n        /**\r\n         * Fired one time when first playing event\r\n         * @method onPlaying\r\n         * @param {Object} event\r\n         */\r\n        onPlaying: function (event) {\r\n            if (event.data.self.autoHideStarted === false) {\r\n                event.data.self.autoHideStarted = true;\r\n                event.data.self.mediaPlayer.getContainer().on('mouseenter touchstart', {\r\n                    self: event.data.self\r\n                }, event.data.self.onPlayerMouseEnter);\r\n                event.data.self.mediaPlayer.getContainer().on('mouseleave touchleave', {\r\n                    self: event.data.self\r\n                }, event.data.self.onPlayerMouseLeave);\r\n            }\r\n        },\r\n        /**\r\n         * Fired one time when first seeking event\r\n         * @method onSeeking\r\n         * @param {Object} event\r\n         */\r\n        onSeeking: function (event) {\r\n            if (event.data.self.autoHideStarted === false) {\r\n                event.data.self.autoHideStarted = true;\r\n                event.data.self.hide();\r\n                event.data.self.mediaPlayer.mediaContainer.on('mouseenter touchstart', {\r\n                    self: event.data.self\r\n                }, event.data.self.onPlayerMouseEnter);\r\n                event.data.self.mediaPlayer.mediaContainer.on('mouseleave touchleave', {\r\n                    self: event.data.self\r\n                }, event.data.self.onPlayerMouseLeave);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on mouse over in progress bar\r\n         * @method onTimeChange\r\n         * @param {Object} event\r\n         */\r\n        onProgressBarMouseover: function () {\r\n            this.timeIndicatorContainer.show();\r\n        },\r\n        /**\r\n         * Fired on mouseout in progress bar\r\n         * @method onTimeChange\r\n         * @param {Object} event\r\n         */\r\n        onProgressBarMouseoout: function () {\r\n            this.timeIndicatorContainer.hide();\r\n        },\r\n        /**\r\n         * Fired on mouse move in progress bar\r\n         * @method onTimeChange\r\n         * @param {Object} event\r\n         */\r\n        onProgressBarMousemove: function (event) {\r\n            var currentTarget = $(event.currentTarget);\r\n            var tooltipMid = this.timeIndicatorContainer.width() / 2;\r\n            var mPos = event.clientX - currentTarget.offset().left;\r\n            var leftPos = Math.max(tooltipMid, Math.min(mPos, currentTarget.width() - tooltipMid));\r\n            var tc = ((mPos * this.mediaPlayer.getDuration()) / currentTarget.width()) + this.mediaPlayer.getTcOffset();\r\n            this.timeIndicatorContainer.css('left', leftPos);\r\n            this.timeIndicatorContainer.find('.ajs-tooltip-text').html(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc, this.settings.framerate, this.settings.timeFormat));\r\n        },\r\n        /**\r\n         * Fired on windows resize\r\n         * @method onWindowResize\r\n         * @param {Object} event\r\n         */\r\n        onWindowResize: function (event) {\r\n            event.data.self.updatePlayerSize(event.data.self.mediaPlayer.getFullscreenState());\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onWindowResize\");\r\n            }\r\n        }\r\n\r\n    });\r\n"
  },
  {
    "path": "src/player/plugins/plugin-base-multi-blocks.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class for multi blocks plugin\n * @class PluginBaseMultiBlocks\n * @namespace fr.ina.amalia.player.plugins\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer Instance of Player\n * @param {Object} pluginContainer plugin container element\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.PluginBaseMultiBlocks\", {\n    METADATA_DISPLAY_TYPE: {\n        'STATIC': 1,\n        'STATIC_DYNAMIC': 2,\n        'DYNAMIC': 3\n    }\n}, {\n    /**\n     * true if load data started anw\n     * @property loadDataStarted\n     * @default null\n     */\n    loadDataStarted: false,\n    /**\n     * List of id to deal\n     * @property loadDataStarted\n     * @default null\n     */\n    dataToDeal: null,\n\n    /**\n     * List of data type managed by this plugin\n     * @property managedDataTypes\n     * @type {String}\n     * @default null\n     */\n    managedMetadataIds: [],\n    /**\n     * List of data id not managed by this plugin\n     * @property managedDataTypes\n     * @type {String}\n     * @default null\n     */\n    notManagedMetadataIds: null,\n    /**\n     * List of data type managed by this plugin\n     * @property managedDataTypes\n     * @type {String}\n     * @default null\n     */\n    listOfMetadataTypes: null,\n    /**\n     * Selected metadata id provide by player core.\n     * @property loadDataStarted\n     * @default ''\n     */\n    selectedMetadataId: '',\n    /**\n     * Add data type to managed data type list\n     * @param {String} type\n     */\n    addManagedMetadataType: function (type) {\n        if (typeof type === 'string' && type !== '') {\n            this.listOfMetadataTypes.push(type);\n        }\n    },\n    /**\n     * Return true if managed type\n     * @param {String} type\n     * @return Boolean\n     */\n    isManagedMetadataType: function (type) {\n        return $.inArray(type, this.listOfMetadataTypes) > -1;\n    },\n    /**\n     * Return managed types\n     * @return array\n     */\n    getManagedMetadataTypes: function () {\n        return this.listOfMetadataTypes;\n    },\n    /**\n     * Return true if managed id\n     * @param {String} id\n     * @return Boolean\n     */\n    isManagedMetadataId: function (id) {\n        return $.inArray(id, this.managedMetadataIds) > -1 || $.inArray(id, this.notManagedMetadataIds) > -1;\n    },\n    /**\n     * Return true if  id\n     * @param {String} id\n     * @method isBound\n     * @return Boolean\n     */\n    isBoundMetadataId: function (id) {\n        return $.inArray(id, this.managedMetadataIds) > -1;\n    },\n    /**\n     * Return bind ids\n     * @method isBound\n     * @return Boolean\n     */\n    getBindIds: function () {\n        return this.managedMetadataIds;\n    },\n    /**\n     * Add data type to managed data type list\n     * @param {String} id\n     */\n    bindMetadataId: function (id) {\n        this.addManagedMetadataId(id);\n    },\n    /**\n     * Add data type to managed data type list\n     * @param {String} id\n     */\n    unbindMetadataId: function (id) {\n        this.removeManagedMetadataId(id);\n    },\n    /**\n     * Add data type to managed data type list\n     * @param {String} id\n     */\n    addManagedMetadataId: function (id) {\n        id = (typeof id === 'number') ? id.toString() : id;\n\n        if (typeof id === 'string' && id !== '' && this.managedMetadataIds.indexOf(id) === -1) {\n            this.managedMetadataIds.push(id);\n            var idx = this.notManagedMetadataIds.indexOf(id);\n            if (idx > -1) {\n                this.notManagedMetadataIds.splice(idx, 1);\n            }\n        }\n    },\n    /**\n     * Add data type to managed data type list\n     * @param {String} id\n     */\n    removeManagedMetadataId: function (id) {\n        id = (typeof id === 'number') ? id.toString() : id;\n        if (typeof id === 'string' && id !== '' && this.notManagedMetadataIds.indexOf(id) === -1) {\n            this.notManagedMetadataIds.push(id);\n            var idx = this.managedMetadataIds.indexOf(id);\n            if (idx > -1) {\n                this.managedMetadataIds.splice(idx, 1);\n            }\n        }\n    },\n\n    /**\n     * Return managed types\n     * @return array\n     */\n    getSelectedMetadata: function () {\n        return this.selectedMetadataId.toString();\n    },\n\n    /**\n     * Fired on selected metadata change\n     * @method onSelectedMetadataChange\n     */\n    onSelectedMetadataChange: function (event, data) {\n        if (data.metadataId !== null) {\n            this.selectedMetadataId = data.metadataId.toString();\n        }\n    }\n});\n"
  },
  {
    "path": "src/player/plugins/plugin-base-simple-block.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class for simple block plugin\n * @class PluginBaseSimpleBlock\n * @namespace fr.ina.amalia.player.plugins\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer Instance of Player\n * @param {Object} pluginContainer plugin container element\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.PluginBaseSimpleBlock\", {}, {\n    /**\n     * List of data type managed by this plugin\n     * @property managedDataTypes\n     * @type {String}\n     * @default null\n     */\n    managedDataType: '',\n    /**\n     * Selected metadata id provide by player core.\n     * @property loadDataStarted\n     * @default ''\n     */\n    selectedMetadataId: '',\n    /**\n     * Add data type to managed data type list\n     * @param type string\n     */\n    addManagedDataType: function (type) {\n        if (typeof type === 'string' && type !== '') {\n            this.managedDataType = type;\n        }\n    },\n    /**\n     * Return true if managed type\n     * @return Boolean\n     */\n    isManagedType: function (type) {\n        return (this.managedDataType === type);\n    },\n\n\n    /**\n     * Return managed types\n     * @return array\n     */\n    getManagedType: function () {\n        return this.managedDataType;\n    },\n\n    /**\n     * Return managed types\n     * @return array\n     */\n    getSelectedMetadata: function () {\n        return this.selectedMetadataId.toString();\n    },\n\n    /**\n     * Fired on selected metadata change\n     * @method onSelectedMetadataChange\n     */\n    onSelectedMetadataChange: function (event, data) {\n        if (data.metadataId !== null) {\n            this.selectedMetadataId = data.metadataId.toString();\n        }\n    }\n});\n"
  },
  {
    "path": "src/player/plugins/plugin-base.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Base class for plugin\r\n * @class PluginBase\r\n * @namespace fr.ina.amalia.player.plugins\r\n * @module player\r\n * @constructor\r\n * @param {Object} settings\r\n * @param {Object} mediaPlayer Instance of Player\r\n * @param {Object} pluginContainer plugin container element\r\n */\r\n$.Class(\"fr.ina.amalia.player.plugins.PluginBase\", {\r\n        classCss: \"plugin-default\",\r\n        style: \"\"\r\n    },\r\n    {\r\n        /**\r\n         * Plugin namespace\r\n         * @property uuid\r\n         * @type {String}\r\n         * @default null\r\n         */\r\n        namespace: null,\r\n        /**\r\n         * universal unique id of this plugins\r\n         * @property uuid\r\n         * @type {String}\r\n         * @default null\r\n         */\r\n        uuid: null,\r\n        /**\r\n         * The element to display media player.\r\n         * @property pluginContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        pluginContainer: null,\r\n        /**\r\n         * Instance of Player HTML5\r\n         * @property mediaPlayer\r\n         * @type {Object} HTMLVideoElement\r\n         * @default null\r\n         */\r\n        mediaPlayer: null,\r\n        /**\r\n         * Instance d'un class logger\r\n         * @property logger\r\n         * @type {Object} HTMLVideoElement\r\n         * @default null\r\n         */\r\n        logger: null,\r\n        /**\r\n         * The element to display loader\r\n         * @property loaderContainer\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        loaderContainer: null,\r\n        /**\r\n         * Configuration\r\n         * @property settings\r\n         * @type {Object}\r\n         * @default \"{}\"\r\n         */\r\n        settings: {},\r\n        /**\r\n         * Event Tag Name\r\n         * @property eventTag\r\n         * @type {String}\r\n         * @default ''\r\n         */\r\n        eventTag: '',\r\n        /**\r\n         * List of plugin Types\r\n         * @property eventTag\r\n         * @type {String}\r\n         * @default ''\r\n         */\r\n        listOfpluginTypes: [],\r\n        bindingManager: null,\r\n        /**\r\n         * @contructor\r\n         * @param {Object} settings\r\n         * @param {Object} mediaPlayer\r\n         * @param {Object} pluginContainer\r\n         */\r\n        init: function (settings, mediaPlayer, pluginContainer, bindingManager) {\r\n            this.mediaPlayer = mediaPlayer;\r\n            this.pluginContainer = $(pluginContainer);\r\n            // Config default values\r\n            this.namespace = this.Class.fullName;\r\n            this.bindingManager = bindingManager;\r\n            this.settings = $.extend({\r\n                    debug: false,\r\n                    internalPlugin: false,\r\n                    shortcuts: {\r\n                        enabled: false,\r\n                        listOfShortcuts: []\r\n                    }\r\n                },\r\n                settings || {});\r\n            this.eventTag = this.Class.fullName + \"_\" + this.pluginContainer.attr('id');\r\n            if (this.settings.internalPlugin === false) {\r\n                this.pluginContainer.addClass(this.Class.classCss);\r\n                this.createLoader();\r\n                this.loaderContainer.show();\r\n            }\r\n            if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\r\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\r\n                    enabled: this.settings.debug\r\n                });\r\n            }\r\n\r\n            if (this.mediaPlayer === null) {\r\n                throw new Error(\"Can't initialize plugin name\" + this.Class.fullName);\r\n            }\r\n            try {\r\n                if (this.settings.internalPlugin === false) {\r\n                    if (this.loaderContainer !== null) {\r\n                        this.loaderContainer.hide();\r\n                    }\r\n                    this.initialize();\r\n                }\r\n                else {\r\n                    this.initialize();\r\n                }\r\n            }\r\n            catch (error) {\r\n                if (this.logger !== null) {\r\n                    this.logger.error(error.stack);\r\n                }\r\n            }\r\n            this.initializeShortcuts();\r\n        },\r\n        /**\r\n         * Initialize\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n\r\n        },\r\n        /**\r\n         * Set uuid\r\n         */\r\n        setUuid: function (uuid) {\r\n            this.uuid = uuid.toString();\r\n            this.pluginContainer.attr('data-plugin-id', this.uuid);\r\n        },\r\n        /**\r\n         * Return uuid genereted by plugin manager\r\n         */\r\n        getUuid: function () {\r\n            return this.uuid.toString();\r\n        },\r\n        /**\r\n         * InitializeError\r\n         * @method initializeError\r\n         * @param {Object} errorCode\r\n         */\r\n        initializeError: function (errorCode) {\r\n            var errorMessage = fr.ina.amalia.player.PlayerErrorCode.getMessage(errorCode);\r\n            var errorContainer = $('<div>', {\r\n                'class': 'ajs-error',\r\n                'style': '',\r\n                'text': errorMessage\r\n            });\r\n            this.pluginContainer.append(errorContainer);\r\n        },\r\n        /**\r\n         * In charge to create loader element for plugin\r\n         * @returns {undefined}\r\n         */\r\n        createLoader: function () {\r\n            this.loaderContainer = $('<div>', {\r\n                'class': 'ajs-loader ajs-icon ajs-icon-cog',\r\n                'style': ''\r\n            });\r\n\r\n            this.loaderContainer.hide();\r\n            this.pluginContainer.append(this.loaderContainer);\r\n        },\r\n        /**\r\n         * In charge to add plugin type\r\n         */\r\n        registerPluginType: function (type) {\r\n            if (typeof type === \"string\" && type !== \"\") {\r\n                this.listOfpluginTypes.push(type);\r\n            }\r\n        },\r\n        /**\r\n         * Return this plugin types\r\n         */\r\n        getPluginTypes: function () {\r\n            return this.listOfpluginTypes;\r\n        },\r\n        /**\r\n         * Fired on plugin is ready\r\n         * @method onPluginReady\r\n         * @param {Object} event\r\n         */\r\n        onPluginReady: function (event) {\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPluginReady\");\r\n            }\r\n            if (event.data.self !== null) {\r\n                event.data.self.initialize();\r\n                if (event.data.self.loaderContainer !== null) {\r\n                    event.data.self.loaderContainer.hide();\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Fired on error event\r\n         * @method onPluginError\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onPluginError: function (event, data) {\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.warn(\"PluginBase/onPluginError\");\r\n            }\r\n            if (event.data.self !== null) {\r\n                var errorCode = (typeof data.errorCode === \"undefined\") ? '' : data.errorCode;\r\n                event.data.self.initializeError(errorCode);\r\n                if (event.data.self.loaderContainer !== null) {\r\n                    event.data.self.loaderContainer.hide();\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * initialize\r\n         * @constructor\r\n         * @method initialize\r\n         */\r\n        initializeShortcuts: function () {\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"initialize shortcuts\");\r\n            }\r\n            if (typeof this.settings.shortcuts === \"object\" && this.settings.shortcuts.enabled === true) {\r\n                // the fetching shortcuts i is element index. e is element as text.\r\n                for (var i = 0; i < this.settings.shortcuts.listOfShortcuts.length; i++) {\r\n                    try {\r\n                        var conf = this.settings.shortcuts.listOfShortcuts[i];\r\n                        if (conf !== null && conf.hasOwnProperty('s') && conf.hasOwnProperty('c')) {\r\n                            var f = conf.c;\r\n                            /* jslint evil: true */\r\n                            var callback = eval('this.' + f);\r\n                            if (typeof callback === \"function\") {\r\n                                // Binding keys\r\n                                $(document).bind('keydown', {\r\n                                    keys: conf.s,\r\n                                    callback: conf.c,\r\n                                    self: this\r\n                                }, this.callback);\r\n                            }\r\n                            else {\r\n                                if (this.logger !== null) {\r\n                                    this.logger.warn(this.Class.fullName + ' : error to bind shortcut ' + conf.c);\r\n                                    this.logger.warn(conf);\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                    catch (error) {\r\n                        if (this.logger !== null) {\r\n                            this.logger.warn(this.Class.fullName + ' : error to bind shortcut ' + conf.c);\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Call shortcut event and prevent default event\r\n         * @param {type} event\r\n         * @param {type} d\r\n         * @returns {undefined}\r\n         */\r\n        callback: function (event) {\r\n            try {\r\n                event.preventDefault();\r\n                /* jslint evil: true */\r\n                eval('event.data.self.' + event.data.callback + '()');\r\n            }\r\n            catch (error) {\r\n                if (event.data.self.logger !== null) {\r\n                    event.data.self.logger.warn(event.data.self.Class.fullName + ' : Error to send callback to bind shortcut :' + event.data.callback);\r\n                }\r\n            }\r\n\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/player/plugins/plugin-binding-manager.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to bind the plugins with data service\n * @class PluginBindingManager\n * @namespace fr.ina.amalia.player.plugins\n * @module player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaPlayer player instance\n */\n$.Class(\"fr.ina.amalia.player.PluginBindingManager\", {\n        dataTypes: {\n            'DEFAULT': 'default',\n            'DETECTION': 'detection',\n            'VISUAL_DETECTION': 'visual_detection',\n            'VISUAL_TRACKING': 'visual_tracking',\n            'SEGMENTATION': 'segmentation',\n            'AUDIO_SEGMENTATION': 'audio_segmentation',\n            'TRANSCRIPTION': 'transcription',\n            'SYNCHRONIZED_TEXT': 'synchronized_text',\n            'KEYFRAMES': 'keyframes',\n            'HISTOGRAM': 'histogram'\n        },\n        pluginTypes: {\n            'TIMELINE_CUEPOINT': 'timeline_cuepoint',\n            'TIMELINE_SEGMENT': 'timeline_segment',\n            'TIMELINE_IMAGE': 'timeline_image',\n            'TIMELINE_HISTOGRAM': 'timeline_histogram',\n            'TIMELINE_VISUAL': 'timeline_visual',\n            'TEXT_SYNC': 'text-sync',\n            'CAPTION': 'caption',\n            'OVERLAY': 'overlay'\n        }\n    },\n    {\n        /**\n         * Instance of Player HTML5\n         * @property mediaPlayer\n         * @type {Object} HTMLVideoElement\n         * @default null\n         */\n        mediaPlayer: null,\n        /**\n         * Logger instance\n         * @property logger\n         * @type {Object} HTMLVideoElement\n         * @default null\n         */\n        logger: null,\n        /**\n         * Configuration\n         * @property settings\n         * @type {Object}\n         * @default \"{}\"\n         */\n        settings: {},\n        /**\n         * List of registered plugins\n         * @property listOfPluginsIds\n         * @type {Object}\n         * @default \"{}\"\n         */\n        listOfDataTypes: null,\n        listOfPluginTypes: null,\n        pluginAndDataTypeMap: null,\n        listOfPlugins: null,\n        /**\n         * Init\n         * @method initialize\n         */\n        init: function (settings, mediaPlayer) {\n            this.listOfPlugins = [];\n            this.mediaPlayer = mediaPlayer;\n            // Settings\n            this.settings = $.extend({\n                    debug: false,\n                    internalPlugin: false\n                },\n                settings || {});\n            // Logger\n            if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            if (this.mediaPlayer === null) {\n                throw new Error(\"Can't initialize plugin name\" + this.Class.fullName);\n            }\n            // Set default values\n            this.setDefaultDataTypes();\n            this.setDefaultPluginAndDataTypeMap();\n            this.setDefaultPluginsTypes();\n        },\n        /**\n         * @method setDefaultDataTypes\n         */\n        setDefaultDataTypes: function () {\n            this.listOfDataTypes = [];\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.DETECTION);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.SEGMENTATION);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.AUDIO_SEGMENTATION);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.TRANSCRIPTION);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.KEYFRAMES);\n            this.listOfDataTypes.push(fr.ina.amalia.player.PluginBindingManager.dataTypes.HISTOGRAM);\n\n        },\n        /**\n         * @method setDefaultPluginsTypes\n         */\n        setDefaultPluginsTypes: function () {\n            this.listOfPluginTypes = [];\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_CUEPOINT);\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT);\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_IMAGE);\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_HISTOGRAM);\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.CAPTION);\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TEXT_SYNC);\n            this.listOfPluginTypes.push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.OVERLAY);\n        },\n        /**\n         * @method setDefaultPluginAndDataTypeMap\n         */\n        setDefaultPluginAndDataTypeMap: function () {\n            this.pluginAndDataTypeMap = [];\n            // DEFAULT\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.DEFAULT] = [];\n\n            // DETECTION\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.DETECTION] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.DETECTION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_CUEPOINT);\n            // VISUAL_DETECTION\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_VISUAL);\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.OVERLAY);\n            // VISUAL_TRACKING\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_VISUAL);\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.OVERLAY);\n            // SEGMENTATION\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.SEGMENTATION] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.SEGMENTATION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT);\n            // AUDIO_SEGMENTATION\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.AUDIO_SEGMENTATION] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.AUDIO_SEGMENTATION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT);\n            // TRANSCRIPTION\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.TRANSCRIPTION] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.TRANSCRIPTION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.CAPTION);\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.TRANSCRIPTION].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TEXT_SYNC);\n            // SYNCHRONIZED_TEXT\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.CAPTION);\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TEXT_SYNC);\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT);\n            // KEYFRAMES\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.KEYFRAMES] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.KEYFRAMES].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_IMAGE);\n            // HISTOGRAM\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.HISTOGRAM] = [];\n            this.pluginAndDataTypeMap[fr.ina.amalia.player.PluginBindingManager.dataTypes.HISTOGRAM].push(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_HISTOGRAM);\n        },\n        /**\n         * Return the line type with data type\n         */\n        getLinetypeWithDataType: function (dataType) {\n            if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.DETECTION) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_CUEPOINT;\n            }\n            else if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.AUDIO_SEGMENTATION) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT;\n            }\n            else if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.SEGMENTATION) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT;\n            }\n            else if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.KEYFRAMES) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_IMAGE;\n            }\n            else if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.HISTOGRAM) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_HISTOGRAM;\n            }\n            else if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT;\n            }\n            else if (dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION || dataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING) {\n                return fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_VISUAL;\n            }\n            return null;\n        },\n        /**\n         * Register plugin\n         * @method registerPlugin\n         * @param id string plugin id\n         * @param type data type\n         */\n        registerPlugin: function (id, listOfTypes) {\n            if (typeof this.listOfPlugins[id] === 'undefined') {\n                this.listOfPlugins[id] = [];\n            }\n            if (typeof listOfTypes === 'object' && listOfTypes.length > 0) {\n                for (var i = 0;\n                     i < listOfTypes.length;\n                     i++) {\n                    if ($.inArray(listOfTypes[i], this.listOfPlugins[id]) < 0) {\n                        this.listOfPlugins[id].push(listOfTypes[i]);\n                    }\n                }\n            }\n        },\n        /**\n         * @method isManagedDataType\n         */\n        isManagedDataType: function (pId, dataType) {\n            var list = this.listOfPlugins[pId];\n            if (typeof list === 'object' && list.length) {\n                for (var i = 0;\n                     i < list.length;\n                     i++) {\n                    if ($.inArray(list[i], this.pluginAndDataTypeMap[dataType]) > -1) {\n                        return true;\n                    }\n                }\n            }\n            return false;\n        }\n    });\n"
  },
  {
    "path": "src/player/plugins/plugin-manager.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * In charge to load plugins.\r\n * @class PluginManager\r\n * @module player\r\n * @submodule player\r\n * @namespace fr.ina.amalia.player.plugins\r\n * @param {Object} settings\r\n * @param {Object} mediaContainer Player main container\r\n * @param {Object} mediaPlayer player instance\r\n */\r\n$.Class(\"fr.ina.amalia.player.plugins.PluginManager\", {}, {\r\n    /**\r\n     * Defines configuration\r\n     * @property settings\r\n     * @type {Object}\r\n     * @default {}\r\n     */\r\n    settings: {},\r\n    /**\r\n     * In charge to render messages in the web console output\r\n     * @property logger\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    logger: null,\r\n    /**\r\n     * Plugin settings\r\n     * @property pluginsSettings\r\n     * @type {Object}\r\n     * @default {}\r\n     */\r\n    pluginsSettings: {},\r\n    /**\r\n     * List of plugins\r\n     * @property plugins\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    plugins: null,\r\n    /**\r\n     * Player dom element\r\n     * @property mediaContainer\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    mediaContainer: null,\r\n    /**\r\n     * Player instance\r\n     * @property mediaPlayer\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    mediaPlayer: null,\r\n    /**\r\n     * Instance of menu contextuel plugin\r\n     * @property contextMenuPlugin\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    contextMenuPlugin: null,\r\n    /**\r\n     * Counts the number plugin loaded.\r\n     * @property loadedCount\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    loadedCount: 0,\r\n    /**\r\n     * Counts the number plugin hasn't loaded.\r\n     * @property failCount\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    failCount: 0,\r\n    /**\r\n     * Counts the number of metadata.\r\n     * @property dataLoaded\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    dataLoaded: 0,\r\n    /**\r\n     * Count of http resource\r\n     * @property httpDataServicesCount\r\n     * @type {Object}\r\n     * @default null\r\n     */\r\n    httpDataServicesCount: 0,\r\n    /**\r\n     * List of plugins ids\r\n     * @property listOfPluginsIds\r\n     * @type {Object}\r\n     * @default []\r\n     */\r\n    listOfPluginIds: [],\r\n    /**\r\n     * List of plugins ids\r\n     * @property listOfPluginsIds\r\n     * @type {Object}\r\n     * @default []\r\n     */\r\n    listOfPluginTypes: [],\r\n    bindingManager: null,\r\n    /**\r\n     * @constructor\r\n     * @param {Object} settings\r\n     * @param {Object} mediaPlayer\r\n     * @param {Object} mediaContainer\r\n     */\r\n    init: function (settings, mediaPlayer, mediaContainer) {\r\n        this.mediaPlayer = mediaPlayer;\r\n        this.mediaContainer = mediaContainer;\r\n        this.httpDataServicesCount = 0;\r\n        this.settings = $.extend({\r\n            debug: false\r\n        }, settings || {});\r\n        this.pluginsSettings = $.extend({\r\n            debug: this.settings.debug,\r\n            dataServices: [],\r\n            list: []\r\n        }, settings.plugins || {});\r\n        if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\r\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\r\n                enabled: this.settings.debug\r\n            });\r\n        }\r\n        this.plugins = [];\r\n        this.initialize();\r\n    },\r\n    /**\r\n     * In charge to initialize plugin Manger\r\n     * @method initialize\r\n     */\r\n    initialize: function () {\r\n        if (this.logger !== null) {\r\n            this.logger.trace(this.Class.fullName, \"initialize\");\r\n        }\r\n        this.bindingManager = new fr.ina.amalia.player.PluginBindingManager(this.settings, this.mediaPlayer);\r\n        this.mediaContainer.one(fr.ina.amalia.player.PlayerEventType.STARTED, {\r\n            self: this\r\n        }, this.onStarted);\r\n        this.mediaContainer.on(fr.ina.amalia.player.PlayerEventType.ERROR, {\r\n            self: this\r\n        }, this.onError);\r\n    },\r\n    loadPlugins: function () {\r\n        // Load static plugins\r\n        var settingsContextMenuPlugin = {\r\n            internalPlugin: true\r\n        };\r\n        this.contextMenuPlugin = this.loadPlugin('fr.ina.amalia.player.plugins.ContextMenuPlugin', settingsContextMenuPlugin, this.mediaPlayer, this.mediaContainer);\r\n\r\n        // default configuration\r\n        var settingsControlBar = $.extend({\r\n            internalPlugin: true,\r\n            framerate: this.settings.framerate,\r\n            debug: this.settings.debug,\r\n            hide: false\r\n        }, this.settings.controlBar || {});\r\n        if (settingsControlBar.hide !== true) {\r\n            this.loadPlugin(this.settings.controlBarClassName, settingsControlBar, this.mediaPlayer, this.mediaContainer);\r\n        }\r\n        // Load dynamic plugins\r\n        this.loadDynamicPlugins(this.pluginsSettings.list);\r\n    },\r\n    /**\r\n     * Return context menu instance\r\n     * @method getContextMenuPlugin\r\n     */\r\n    getContextMenuPlugin: function () {\r\n        return this.contextMenuPlugin;\r\n    },\r\n    /**\r\n     * Fired on load started event\r\n     * @method onStarted\r\n     * @param {Object} event\r\n     */\r\n    onStarted: function (event) {\r\n        event.data.self.loadPlugins();\r\n        event.data.self.loadData(event.data.self.pluginsSettings.dataServices);\r\n    },\r\n    /**\r\n     * Fired on error event\r\n     * @method onError\r\n     * @param {Object} event\r\n     */\r\n    onError: function (event) {\r\n        event.data.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PLUGIN_ERROR, {\r\n            errorCode: fr.ina.amalia.player.PlayerErrorCode.ERROR_LOAD_PLUGIN\r\n        });\r\n    },\r\n    /**\r\n     * In charge to load data.\r\n     * @method loadData\r\n     * @param {Array} urls List of plugin resource\r\n     */\r\n    loadData: function (hosts) {\r\n        // Send BEGIN_DATA_CHANGE event\r\n        this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE);\r\n\r\n        if ($.isArray(hosts)) {\r\n            var i = 0;\r\n            var loader = null;\r\n            var host = null;\r\n            var settings = {\r\n                debug: this.settings.debug\r\n            };\r\n            for (i = 0;\r\n                 i < hosts.length;\r\n                 ++i) {\r\n                loader = null;\r\n                host = hosts[i];\r\n                settings = {\r\n                    debug: this.pluginsSettings.debug,\r\n                    format: host.hasOwnProperty('format') ? host.format : 'json',\r\n                    url: host.hasOwnProperty('url') ? host.url : host,\r\n                    parameters: (host.hasOwnProperty('parameters')) ? host.parameters : null,\r\n                    sublocalisations: (host.hasOwnProperty('sublocalisations')) ? host.sublocalisations : false\r\n                };\r\n\r\n                if (typeof host[i] === 'string') {\r\n                    loader = new fr.ina.amalia.player.HttpLoader(settings, this.mediaPlayer, this.dataLoadedHandler, {\r\n                        self: this\r\n                    });\r\n                }\r\n                else if (host.hasOwnProperty('protocol')) {\r\n                    if (host.protocol === \"http\") {\r\n                        loader = new fr.ina.amalia.player.HttpLoader(settings, this.mediaPlayer, this.dataLoadedHandler, {\r\n                            self: this\r\n                        });\r\n                    }\r\n                    else if (host.protocol === \"ws\") {\r\n                        loader = new fr.ina.amalia.player.WsLoader(settings, this.mediaPlayer, this.dataLoadedHandler, {\r\n                            self: this\r\n                        });\r\n                    }\r\n                    else {\r\n                        try {\r\n                            // In charge to instantiate custom class\r\n                            /* jslint evil: true */\r\n                            loader = eval('new ' + host.protocol + '(settings, this.mediaPlayer, this.dataLoadedHandler,{self:this});');\r\n                        }\r\n                        catch (error) {\r\n                            if (this.logger !== null) {\r\n                                this.logger.warn(\"Unknown host configuration type.\");\r\n                                this.logger.error(error.stack);\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n                else {\r\n                    this.logger.warn(\"Unknown host configuration.\");\r\n                    this.logger.warn(host);\r\n                }\r\n                // Necessary for to start the plugin ready event\r\n                if (loader !== null && loader.getWaitLoadEvent()) {\r\n                    this.httpDataServicesCount++;\r\n                }\r\n            }\r\n        }\r\n        else {\r\n            if (this.logger !== null) {\r\n                this.logger.warn(\"Error dataServices is not array.\");\r\n            }\r\n        }\r\n    },\r\n    /**\r\n     * Méthode en charge de traiter les chargements de métadata.\r\n     * @method loadDynamicPlugins\r\n     * @param {Object} event\r\n     * @param {String} status\r\n     * @event fr.ina.amalia.player.PlayerEventType.PLUGIN_READY\r\n     */\r\n    dataLoadedHandler: function (event, status) {\r\n        if (status !== \"success\") {\r\n\r\n            if (this.logger !== null) {\r\n                this.logger.warn(\"Failed to load data. status: \" + status);\r\n                this.logger.warn(event);\r\n            }\r\n            event.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PLUGIN_ERROR, {\r\n                errorCode: fr.ina.amalia.player.PlayerErrorCode.ERROR_LOAD_PLUGIN\r\n            });\r\n        }\r\n        event.self.dataLoaded++;\r\n        if (event.self.dataLoaded === event.self.httpDataServicesCount) {\r\n            // Event BEGIN_DATA_CHANGE\r\n            event.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE);\r\n            // Deprecated\r\n            event.self.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PLUGIN_READY);\r\n        }\r\n    },\r\n    /**\r\n     * In charge to load plugins\r\n     * @method loadDynamicPlugins\r\n     * @param {Object} list list of plugin resource\r\n     */\r\n    loadDynamicPlugins: function (list) {\r\n        if (this.logger !== null) {\r\n            this.logger.info(this.Class.fullName, \"loadPlugins List : \");\r\n            this.logger.info(list);\r\n        }\r\n        for (var item in list) {\r\n            if (list.hasOwnProperty(item)) {\r\n                var pluginSettings = list[item];\r\n                pluginSettings = $.extend({\r\n                        debug: this.pluginsSettings.debug,\r\n                        className: null,\r\n                        container: null,\r\n                        data: this.data,\r\n                        framerate: this.settings.framerate,\r\n                        thumbRootDirectory: this.settings.thumbRootDirectory,\r\n                        parameters: {}\r\n                    },\r\n                    pluginSettings || {});\r\n                if (typeof pluginSettings === \"object\" && pluginSettings.className !== null) {\r\n                    var pluginContainer = (pluginSettings.container === null) ? this.mediaContainer : $(pluginSettings.container);\r\n                    pluginSettings.internalPlugin = (pluginSettings.container === null);\r\n                    this.loadPlugin(pluginSettings.className, pluginSettings, this.mediaPlayer, pluginContainer);\r\n                }\r\n                else {\r\n                    if (this.logger !== null) {\r\n                        this.logger.warn(this.Class.fullName, \"Error to load plugin className:\" + pluginSettings.className);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    },\r\n    /**\r\n     * In charge to load plugin\r\n     * @method loadPlugin\r\n     * @param {Object} className plugin class name\r\n     * @param {Object} settings plugin configuration\r\n     * @param {Object} mediaPlayer player instance\r\n     * @param {Object} pluginContainer plugin dom element\r\n     * @return {Boolean}\r\n     */\r\n    loadPlugin: function (className, settings, mediaPlayer, pluginContainer) {\r\n\r\n        if (this.logger !== null) {\r\n            this.logger.info(this.Class.fullName, \"loadPlugin className:\" + className);\r\n        }\r\n\r\n        try {\r\n            /* jslint evil: true */\r\n            /* jshint unused:false */\r\n            var obj = eval('new ' + className + '(settings,mediaPlayer,pluginContainer,this.bindingManager)');\r\n            obj.setUuid(fr.ina.amalia.player.helpers.UtilitiesHelper.generateUUID('p_'));\r\n\r\n            this.bindingManager.registerPlugin(obj.getUuid(), obj.getPluginTypes());\r\n\r\n            this.listOfPluginIds.push(obj.getUuid());\r\n            this.listOfPluginTypes[obj.getUuid()] = obj.getPluginTypes();\r\n\r\n            this.plugins.push(obj);\r\n            this.loadedCount++;\r\n            if (this.logger !== null) {\r\n                this.logger.info(className + \"Plugin loaded :  \" + className + \" ID: \" + obj.getUuid());\r\n            }\r\n            return obj;\r\n        }\r\n        catch (error) {\r\n            this.failCount++;\r\n            if (this.logger !== null) {\r\n                this.logger.warn(\"Error to load plugin : \" + className);\r\n                this.logger.warn(error.stack);\r\n            }\r\n            return false;\r\n        }\r\n    }\r\n});\r\n"
  },
  {
    "path": "src/player/shortcuts-manager.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n$.Class(\"fr.ina.amalia.player.ShortcutsManager\", {}, {\n    /**\n     * Instance of Player HTML5\n     * @property mediaPlayer\n     * @type {Object} HTMLVideoElement\n     * @default null\n     */\n    mediaPlayer: null,\n    /**\n     * Defines configuration\n     * @property settings\n     * @type {Object}\n     * @default {}\n     */\n    settings: {},\n    /**\n     * In charge to render messages in the web console output\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    logger: null,\n    listOfShortcuts: null,\n    init: function (settings, mediaPlayer) {\n        this.mediaPlayer = mediaPlayer;\n        this.settings = $.extend({\n            enabled: false,\n            debug: false,\n            listOfShortcuts: [\n                {'s': 'f1', 'c': 'togglePlayPause'},\n                {'s': 'f11', 'c': 'toggleFullScreen'},\n                {'s': 'f2', 'c': 'muteUnmute'},\n                {'s': 'f3', 'c': 'seekToBegin'},\n                {'s': 'f4', 'c': 'seekToEnd'}\n            ]\n        }, settings || {});\n        this.listOfShortcuts = this.settings.listOfShortcuts;\n\n        if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n        this.initialize();\n    },\n    /**\n     * initialize\n     * @constructor\n     * @method initialize\n     */\n    initialize: function () {\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"initialize\");\n        }\n        if (this.settings.enabled === true) {\n            // the fetching shortcuts i is element index. e is element as text.\n            for (var i = 0; i < this.listOfShortcuts.length; i++) {\n                try {\n                    var conf = this.listOfShortcuts[i];\n                    if (conf !== null && conf.hasOwnProperty('s') && conf.hasOwnProperty('c')) {\n                        var f = conf.c;\n                        /* jslint evil: true */\n                        var callback = eval('this.mediaPlayer.' + f);\n                        if (typeof callback === \"function\") {\n                            // Binding keys\n                            $(document).bind('keydown', {keys: conf.s, callback: conf.c, self: this}, this.callback);\n                        }\n                        else {\n                            if (this.logger !== null) {\n                                this.logger.warn(this.Class.fullName + ' : error to bind shortcut ' + conf.c);\n                                this.logger.warn(conf);\n                            }\n                        }\n                    }\n                }\n                catch (error) {\n                    if (this.logger !== null) {\n                        this.logger.warn(this.Class.fullName + ' : error to bind shortcut ' + conf.c);\n                    }\n                }\n            }\n        }\n    },\n    /**\n     * Call shortcut event and prevent default event\n     * @param {type} event\n     * @param {type} d\n     * @returns {undefined}\n     */\n    callback: function (event) {\n        try {\n            event.preventDefault();\n            /* jslint evil: true */\n            eval('event.data.self.mediaPlayer.' + event.data.callback + '()');\n        }\n        catch (error) {\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.warn(event.data.self.Class.fullName + ' : Error to send callback to bind shortcut :' + event.data.callback);\n            }\n        }\n\n    }\n});"
  },
  {
    "path": "src/player/widgets/base-button.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Base button for control bar with callback function\r\n * @class BaseButton\r\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\r\n * @module player\r\n * @submodule player-controlbar\r\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\r\n */\r\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.BaseButton\", {\r\n        classCss: \"player-base-button\",\r\n        style: \"\"\r\n    },\r\n    {\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            // Create component\r\n            this.component = $('<div>', {\r\n                'class': this.Class.classCss,\r\n                'style': this.Class.style\r\n            });\r\n            var buttonContainer = $(\"<span>\", {\r\n                'class': \"button-container\"\r\n            });\r\n            var icon = $('<span>', {\r\n                'class': this.parameter.style\r\n            });\r\n            if (this.parameter.hasOwnProperty('title') === true) {\r\n                icon.attr('title', this.parameter.title);\r\n            }\r\n            buttonContainer.append(icon);\r\n            // set events\r\n            this.component.on('click', {\r\n                self: this,\r\n                component: this.component\r\n            }, this.onClick);\r\n            this.component.append(buttonContainer);\r\n            // Add to container\r\n            this.container.append(this.component);\r\n\r\n        },\r\n        /**\r\n         * On click event fire callback function\r\n         * @method onClick\r\n         * @param {Object} event\r\n         */\r\n        onClick: function (event) {\r\n            if (typeof event.data.self.parameter.callback !== \"undefined\") {\r\n                try {\r\n                    /* jslint evil: true */\r\n                    eval(event.data.self.parameter.callback + '(event.data.self.mediaPlayer, event.data.self.parameter)');\r\n                }\r\n                catch (e) {\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.warn(\"Send callback failed.\");\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/player/widgets/cast-button.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Cast widget\n * @class Cast\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\n * @module player\n * @submodule player-controlbar\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\n */\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.CastButton\", {\n        classCss: \"iplayer-cast cast-default-button\",\n        style: \"\",\n        eventTypes: {},\n        STYLED_MEDIA_RECEIVER_APP_ID: '54A187F2' // default app ID to the default media receiver app\n    },\n    {\n\n        logger: null,\n        /**\n         * Cast initialization timer delay\n         **/\n        CAST_API_INITIALIZATION_DELAY: 1000,\n        /**\n         * Progress bar update timer delay\n         **/\n        PROGRESS_BAR_UPDATE_DELAY: 1000,\n        /**\n         * Session idle time out in miliseconds\n         **/\n        SESSION_IDLE_TIMEOUT: 300000,\n        CAST_STATE_ACTIVE: 'ajs-icon ajs-icon-chromecast-active',\n        CAST_STATE_IDLE: 'ajs-icon ajs-icon-chromecast',\n        CAST_STATE_WARNING: '',\n        //join session\n        session: null,\n        storedSession: null,\n        joinsessionbyid: null,\n        timer: null,\n        currentMediaSession: null,\n        mediaCurrentTime: 0,\n        currentMediaTime: 0,\n        progressFlag: 1,\n        /**\n         * Initialize the component\n         * @method initialize\n         */\n        initialize: function () {\n            this.session = null;\n            this.storedSession = null;\n            this.joinsessionbyid = null;\n            this.timer = null;\n            this.currentMediaSession = null;\n            this.mediaCurrentTime = 0;\n            this.currentMediaTime = 0;\n            this.progressFlag = 1;\n            // Create component\n            this.component = $('<div>', {\n                'class': this.Class.classCss,\n                'style': this.Class.style\n            });\n            // Add to container\n            this.container.append(this.component);\n            /**\n             * Call initialization\n             */\n            if (chrome.cast || chrome.cast.isAvailable) {\n                this.createButton();\n                setTimeout($.proxy(this.initializeCastApi, this), this.CAST_API_INITIALIZATION_DELAY);\n                this.definePlayerEvents();\n            }\n            else {\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"chrome.cast is not available\");\n                }\n            }\n\n        },\n        /**\n         * Add player events for play button\n         * @method definePlayerEvents\n         */\n        definePlayerEvents: function () {\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.CAST_PLAYING, $.proxy(this.onPlaying, this));\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.CAST_PAUSED, $.proxy(this.onPaused, this));\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.SEEKING, $.proxy(this.onSeek, this));\n        },\n        /**\n         * Create button for chrome cast control\n         */\n        createButton: function () {\n            var buttonContainer = $(\"<span>\", {\n                'class': \"button-container\",\n                'style': 'font-size: 30px; color: white;'\n            });\n            var icon = $('<i>', {\n                'class': this.CAST_STATE_IDLE\n            });\n            buttonContainer.append(icon);\n            // set events\n            this.component.on('click', $.proxy(this.onClickToCast, this));\n            this.component.append(buttonContainer);\n            // Add to container\n            this.container.append(this.component);\n        },\n        /**\n         * Initialize chrome cast API\n         */\n        initializeCastApi: function () {\n            // auto join policy can be one of the following three\n            // 1) no auto join\n            // 2) same appID, same URL, same tab\n            // 3) same appID and same origin URL\n            var autoJoinPolicyArray = [\n                chrome.cast.AutoJoinPolicy.PAGE_SCOPED,\n                chrome.cast.AutoJoinPolicy.TAB_AND_ORIGIN_SCOPED,\n                chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED\n            ];\n            // request session\n            var sessionRequest = new chrome.cast.SessionRequest(this.Class.STYLED_MEDIA_RECEIVER_APP_ID);\n            var apiConfig = new chrome.cast.ApiConfig(sessionRequest,\n                $.proxy(this.sessionListener, this),\n                $.proxy(this.receiverListener, this),\n                autoJoinPolicyArray[1]);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n            chrome.cast.initialize(apiConfig, $.proxy(this.onInitSuccess, this), $.proxy(this.onError, this));\n        },\n        /**\n         * Load media\n         * @param {string} mediaURL media URL string\n         * @this loadMedia\n         */\n        loadMedia: function (mediaURL, currentTime) {\n            if (!this.session) {\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, 'no session');\n                }\n                return;\n            }\n\n            if (mediaURL) {\n                var mediaInfo = new chrome.cast.media.MediaInfo(mediaURL);\n                var currentMediaTitle = this.mediaPlayer.getTitle();\n                var currentMediaThumb = this.mediaPlayer.getPoster();\n                mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();\n                mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;\n                mediaInfo.contentType = this.mediaPlayer.getContentType();\n                if (currentMediaTitle !== \"\") {\n                    mediaInfo.metadata.title = currentMediaTitle;\n                }\n                if (currentMediaThumb !== \"\") {\n                    mediaInfo.metadata.images = [{'url': currentMediaThumb}];\n                }\n                var request = new chrome.cast.media.LoadRequest(mediaInfo);\n                request.autoplay = true;\n                request.currentTime = currentTime;\n                this.session.loadMedia(request, this.onMediaDiscovered.bind(this, 'loadMedia'),\n                    $.proxy(this.onMediaError, this));\n                this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.CAST_PLAYING);\n            }\n        },\n        /**\n         * Set Current time\n         * @param currentTime\n         */\n        setCurrentTime: function (currentTime) {\n            if (this.currentMediaSession !== null) {\n                var request = new chrome.cast.media.SeekRequest();\n                request.currentTime = currentTime;\n                this.currentMediaSession.seek(request);\n            }\n        },\n        /**\n         * Initialization success callback\n         */\n        onInitSuccess: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'init success');\n            }\n            // check if a session ID is saved into localStorage\n            this.storedSession = JSON.parse(localStorage.getItem('storedSession'));\n        },\n\n        /**\n         * Generic error callback\n         * @param {Object} e A chrome.cast.Error object.\n         */\n        onMediaError: function (e) {\n            this.mediaPlayer.setCastMode(false);\n            this.mediaPlayer.stop();\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onMediaError: \" + e);\n            }\n        },\n\n        /**\n         * Generic error callback\n         * @param {Object} e A chrome.cast.Error object.\n         */\n        onError: function (e) {\n            this.mediaPlayer.setCastMode(false);\n            this.mediaPlayer.stop();\n            this.component.find('i').attr('class', this.CAST_STATE_IDLE);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, e);\n            }\n        },\n\n        /**\n         * Generic success callback\n         * @param {string} message from callback\n         */\n        onSuccess: function (message) {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, message);\n            }\n        },\n\n        /**\n         * Callback on success for stopping app\n         */\n        onStopAppSuccess: function () {\n            this.mediaPlayer.setCastMode(false);\n            this.mediaPlayer.stop();\n            this.component.find('i').attr('class', this.CAST_STATE_IDLE);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'Session stopped');\n            }\n        },\n\n        /**\n         * Session listener during initialization\n         * @param {Object} e session object\n         * @this sessionListener\n         */\n        sessionListener: function (e) {\n            this.session = e;\n            this.component.find('i').attr('class', this.CAST_STATE_IDLE);\n            if (this.session.media.length !== 0) {\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, 'Found ' + this.session.media.length + ' existing media sessions.');\n                }\n                this.onMediaDiscovered('sessionListener', this.session.media[0]);\n            }\n            this.session.addMediaListener(this.onMediaDiscovered.bind(this, 'addMediaListener'));\n            this.session.addUpdateListener(this.sessionUpdateListener.bind(this));\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'New session ID: ' + e.sessionId);\n            }\n        },\n        /**\n         * Receiver listener during initialization\n         * @param {string} e status string from callback\n         */\n        receiverListener: function (e) {\n            if (e === 'available') {\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, 'receiver found');\n                }\n            }\n            else {\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, 'receiver list empty : ' + e);\n                }\n            }\n        },\n        /**\n         * Launch app and request session\n         */\n        launchApp: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'launching app...');\n            }\n            chrome.cast.requestSession($.proxy(this.onRequestSessionSuccess, this), $.proxy(this.onLaunchError, this));\n            if (this.timer) {\n                clearInterval(this.timer);\n            }\n        },\n        /**\n         * In charge to stop casting\n         */\n        stopCasting: function () {\n            if (this.session !== null) {\n                this.session.stop(this.onStopAppSuccess.bind(this), $.proxy(this.onError, this));\n            }\n        },\n\n        /**\n         * Callback on success for requestSession call\n         * @param {Object} e A non-null new session.\n         * @this onRequestSesionSuccess\n         */\n        onRequestSessionSuccess: function (e) {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'session success: ' + e.sessionId);\n            }\n            this.session = e;\n            this.session.addUpdateListener(this.sessionUpdateListener.bind(this));\n            if (this.session.media.length !== 0) {\n                this.onMediaDiscovered('onRequestSession', this.session.media[0]);\n            }\n            this.session.addMediaListener(this.onMediaDiscovered.bind(this, 'addMediaListener'));\n\n            this.loadMedia(this.mediaPlayer.getSrc(), this.mediaPlayer.getCurrentTime());\n        },\n        /**\n         * Session update listener\n         * @param {boolean} isAlive status from callback\n         * @this sessionUpdateListener\n         */\n        sessionUpdateListener: function (isAlive) {\n            if (!isAlive) {\n                this.session = null;\n                this.component.find('i').attr('class', this.CAST_ICON_THUMB_IDLE);\n            }\n        },\n        /**\n         * Callback on success for loading media\n         * @param {string} how info string from callback\n         * @param {Object} mediaSession media session object\n         * @this onMediaDiscovered\n         */\n        onMediaDiscovered: function (how, mediaSession) {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'new media session ID:' + mediaSession.mediaSessionId + ' (' + how + ')');\n            }\n            this.currentMediaSession = mediaSession;\n            this.currentMediaSession.addUpdateListener($.proxy(this.onMediaStatusUpdate, this));\n            this.mediaCurrentTime = this.currentMediaSession.currentTime;\n        },\n        /**\n         * Callback for media status event\n         * @param {boolean} isAlive status from callback\n         */\n        onMediaStatusUpdate: function (apiMedia) {\n            if (!apiMedia) {\n                if (this.currentMediaSession.playerState === 'IDLE') {\n                    this.stopCasting();\n                }\n            }\n            else {\n                this.mediaPlayer.setCastMode(true);\n                this.component.find('i').attr('class', this.CAST_STATE_ACTIVE);\n            }\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'currentMediaSession.playerState :' + this.currentMediaSession.playerState);\n            }\n        },\n        /**\n         * Callback on launch error\n         */\n        onLaunchError: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, 'launch error');\n            }\n        },\n        /**\n         * Callback on click to cast button\n         */\n        onClickToCast: function () {\n            if (this.session === null) {\n                this.launchApp();\n            }\n        },\n        /**\n         * Fired on playing event for hide play button.\n         * @method onPlaying\n         * @param {Object} event\n         */\n        onPlaying: function () {\n            if (this.currentMediaSession !== null) {\n                this.mediaPlayer.stop();\n                var playRequest = new chrome.cast.media.PlayRequest();\n                this.currentMediaSession.play(playRequest);\n            }\n        },\n        /**\n         * Fired on paused event for show play button.\n         * @method onPaused\n         * @param {Object} event\n         */\n        onPaused: function () {\n            if (this.currentMediaSession !== null) {\n                this.mediaPlayer.stop();\n                var pauseRequest = new chrome.cast.media.PauseRequest();\n                this.currentMediaSession.play(pauseRequest);\n            }\n        },\n\n        onSeek: function (event, data) {\n            if (this.currentMediaSession !== null) {\n                var duration = this.mediaPlayer.getDuration();\n                var tc = (duration * data.percentage) / 100;\n                this.setCurrentTime(tc);\n            }\n        }\n    })\n;\n"
  },
  {
    "path": "src/player/widgets/channel-volume-control-bar.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Control the volume of each channel\n * @class ChannelVolumeControlBar\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\n * @module player\n * @submodule player-controlbar\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\n */\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.ChannelVolumeControlBar\", {\n        mainClassCss: \"player-channel-volume-control-position\",\n        classCss: \"player-channel-volume-control\",\n        classCssVolumeOn: \"ajs-icon ajs-icon-controlbar-volume_max\",\n        classCssVolumeDown: \"ajs-icon ajs-icon-controlbar-volume-min\",\n        classCssVolumeOff: \"ajs-icon ajs-icon-controlbar-volume-off\",\n        classUnifyVolumeStateOn: \"ajs-icon-sound-link-on\",\n        classUnifyVolumeStateOff: \"ajs-icon-sound-link-off\",\n        classLeftVolumeStateOn: \"ajs-icon-controlbar-volume-left\",\n        classLeftVolumeStateOff: \"ajs-icon-controlbar-volume-left-off\",\n        classRightVolumeStateOn: \"ajs-icon-controlbar-volume-right\",\n        classRightVolumeStateOff: \"ajs-icon-controlbar-volume-right-off\",\n        style: \"\",\n        eventTypes: {\n            CLICK: \"fr.ina.amalia.player.plugins.widgets.VolumeControlBar.event.click\",\n            CHANGE: \"fr.ina.amalia.player.plugins.widgets.VolumeControlBar.event.change\",\n            HOVER: \"fr.ina.amalia.player.plugins.widgets.VolumeControlBar.event.hover\"\n        }\n    },\n    {\n        /**\n         * Slide start value\n         * @property sliderStart\n         * @type {Object}\n         * @default 0\n         */\n        sliderStart: 0,\n        /**\n         * Slide end value\n         * @property sliderEnd\n         * @type {Object}\n         * @default 100\n         */\n        sliderEnd: 100,\n        /**\n         * Default value\n         * @property defaultValue\n         * @type {Object}\n         * @default 100\n         */\n        defaultValue: 100,\n        /**\n         * Volume component element\n         * @property volumeControlComponent\n         * @type {Object}\n         * @default null\n         */\n        volumeControlComponent: null,\n        /**\n         * AudioContext interface represents\n         * @property audioContext\n         * @type {Object}\n         * @default null\n         */\n        audioContext: null,\n        /**\n         * Pan Left\n         * @property panLeft\n         * @type {Object}\n         * @default null\n         */\n        panLeft: null,\n        /**\n         * Pan Right\n         * @property panRight\n         * @type {Object}\n         * @default null\n         */\n        panRight: null,\n\n        /***\n         * Specific if channel is merged\n         */\n        channelMerger: false,\n        /***\n         * Specific channel to merge\n         */\n        channelMergerNode: null,\n        unifyVolumeState: true,\n        /**\n         * Initialize the component\n         * @method initialize\n         */\n        initialize: function () {\n            this.defaultValue = 100;\n            this.unifyVolumeState = true;\n            this.channelMerger = (this.parameter.hasOwnProperty('channelMerger') === true) ? this.parameter.channelMerger : true;\n            this.channelMergerNode = (this.parameter.hasOwnProperty('channelMergerNode') === true && this.parameter.channelMergerNode !== \"\" ) ? this.parameter.channelMergerNode : null;\n            // Create component\n            var mainContainer = $('<div>', {\n                'class': this.Class.mainClassCss\n            });\n            // Create component\n            this.component = $('<div>', {\n                'class': this.Class.classCss,\n                'style': this.Class.style\n            });\n\n            try {\n                this.mediaPlayer.setVolume(100);\n                var AudioContext = window.AudioContext || window.webkitAudioContext;\n                this.audioContext = new AudioContext();\n                // this.audioContext.createGain = this.audioContext.createGain || this.audioContext.createGainNode; //fallback for gain naming\n                this.setupAudioNodes();\n            }\n            catch (e) {\n                this.audioContext = null;\n                if (this.logger !== null) {\n                    this.logger.warn(e);\n                }\n            }\n\n            if (this.audioContext !== null) {\n                this.createChannelVolume();\n                this.definePlayerEvents();\n                this.updateVolume();\n            }\n\n            // Add to container\n            mainContainer.append( this.component);\n            this.container.append(mainContainer);\n\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n        },\n        /**\n         * In charge to init audio context\n         * @returns {undefined}\n         */\n        setupAudioNodes: function () {\n            //Connect to source\n            var source = this.audioContext.createMediaElementSource(this.mediaPlayer.getMediaPlayer().get(0));\n            this.panLeft = this.audioContext.createGain();\n            this.panRight = this.audioContext.createGain();\n            var splitter = this.audioContext.createChannelSplitter(2);\n            //Connect the source to the splitter\n            source.connect(splitter, 0, 0);\n            //Connect splitter' outputs to each Gain Nodes\n            splitter.connect(this.panLeft, 0);\n            splitter.connect(this.panRight, 1);\n            if (this.channelMergerNode !== null) {\n                var panner = this.audioContext.createPanner();\n                panner.coneOuterGain = 1;\n                panner.coneOuterAngle = 180;\n                panner.coneInnerAngle = 0;\n                if (this.channelMergerNode === 'l') {\n                    panner.setPosition(-1, 0, 0);\n                }\n                else if (this.channelMergerNode === 'r') {\n                    panner.setPosition(1, 0, 0);\n                }\n                this.panLeft.connect(panner);\n                this.panRight.connect(panner);\n                panner.connect(this.audioContext.destination);\n            }\n            else {\n                if (this.channelMerger === true) {\n                    //Connect Left and Right Nodes to the output\n                    //Assuming stereo as initial status\n                    this.panLeft.connect(this.audioContext.destination, 0);\n                    this.panRight.connect(this.audioContext.destination, 0);\n                }\n                else if (this.channelMergerNode === null) {\n                    //Create a merger node, to get both signals back together\n                    var merger = this.audioContext.createChannelMerger(2);\n\n                    //Connect both channels to the Merger\n                    this.panLeft.connect(merger, 0, 0);\n                    this.panRight.connect(merger, 0, 1);\n\n                    //Connect the Merger Node to the final audio destination\n                    merger.connect(this.audioContext.destination);\n                }\n            }\n        },\n        /**\n         * In charge to create channel volume container\n         */\n        createChannelVolume: function () {\n            var self = this;\n\n            var volumeControlBtn = $('<div>', {\n                'class': 'volume-control-btn'\n            });\n\n            volumeControlBtn.on('click', {\n                self: this\n            }, this.onClickToVolume);\n            //add volume bouton\n            this.component.append(volumeControlBtn);\n\n            var channelVolumeSlidersContainer = $('<div>', {\n                'class': 'channel-volume-sliders'\n            });\n\n            channelVolumeSlidersContainer.on('mouseleave', {\n                self: this\n            }, this.onChannelVolumeMouseout);\n            //Block Info\n            var channelVolumeInfoContainer = $('<div>', {\n                'class': 'channel-volume-info'\n            });\n            channelVolumeSlidersContainer.append(channelVolumeInfoContainer);\n            var channelInfoLeftContainer = $('<div>', {\n                'class': 'channel-volume-info-left'\n            });\n            channelInfoLeftContainer.on('click', {self: this}, this.onClickLeftVolume);\n            var channelInfoLeft = $('<span>', {\n                'class': 'text ajs-icon ' + this.Class.classLeftVolumeStateOn,\n                'text': ''\n            });\n            channelInfoLeftContainer.append(channelInfoLeft);\n            channelVolumeInfoContainer.append(channelInfoLeftContainer);\n            //Mid\n            var channelInfoMidContainer = $('<div>', {\n                'class': 'channel-volume-info-mid'\n            });\n            var channelInfoUnify = $('<span>', {\n                'class': 'unify on ajs-icon ' + this.Class.classUnifyVolumeStateOn,\n                'text': ''\n            });\n            channelInfoUnify.on('click', {\n                self: this\n            }, this.onClickUnifyVolumeState);\n\n            channelInfoMidContainer.append(channelInfoUnify);\n            channelVolumeInfoContainer.append(channelInfoMidContainer);\n            var channelInfoRightContainer = $('<div>', {\n                'class': 'channel-volume-info-right'\n            });\n            channelInfoRightContainer.on('click', {self: this}, this.onClickRightVolume);\n            var channelInfoRight = $('<span>', {\n                'class': 'text ajs-icon ' + this.Class.classRightVolumeStateOn,\n                'text': ''\n            });\n            channelInfoRightContainer.append(channelInfoRight);\n            channelVolumeInfoContainer.append(channelInfoRightContainer);\n            //Block Control\n            var channelVolumeControlContainer = $('<div>', {\n                'class': 'channel-volume-control'\n            });\n            channelVolumeSlidersContainer.append(channelVolumeControlContainer);\n            var channelControlLeftContainer = $('<div>', {\n                'class': 'channel-volume-control-left'\n            });\n            //Left slider\n            var leftVolumeSlider = $('<div>', {\n                'class': 'left-volume-slider'\n            });\n            leftVolumeSlider.slider({\n                orientation: \"vertical\",\n                min: 0,\n                max: 100,\n                value: 100,\n                slide: function (event, ui) {\n                    self.panLeft.gain.value = ui.value / 100;\n                    if (self.unifyVolumeState === true) {\n                        self.panRight.gain.value = ui.value / 100;\n                        self.component.find('.right-volume-slider').slider(\"value\", ui.value);\n                    }\n                    self.updateVolume();\n                },\n                change: function (event, ui) {\n                    self.updateVolume();\n                    self.panLeft.gain.value = ui.value / 100;\n                    var rv = self.component.find('.right-volume-slider').slider(\"value\");\n                    if (self.unifyVolumeState === true && rv !== ui.value) {\n                        self.panRight.gain.value = ui.value / 100;\n                        self.component.find('.right-volume-slider').slider(\"value\", ui.value);\n                    }\n                }\n            });\n            channelControlLeftContainer.append(leftVolumeSlider);\n            channelVolumeControlContainer.append(channelControlLeftContainer);\n            //Mid\n            var channelControlMidContainer = $('<div>', {\n                'class': 'channel-volume-control-mid on'\n            });\n            channelVolumeControlContainer.append(channelControlMidContainer);\n            //Right\n            var channelControlRightContainer = $('<div>', {\n                'class': 'channel-volume-control-right'\n            });\n            //Right slider\n            var rightVolumeSlider = $('<div>', {\n                'class': 'right-volume-slider'\n            });\n            rightVolumeSlider.slider({\n                orientation: \"vertical\",\n                min: 0,\n                max: 100,\n                value: 100,\n                slide: function (event, ui) {\n                    self.panRight.gain.value = ui.value / 100;\n                    if (self.unifyVolumeState === true) {\n                        self.panLeft.gain.value = ui.value / 100;\n                        self.component.find('.left-volume-slider').slider(\"value\", ui.value);\n                        var offset = $(event.target).position().top + $(ui.handle).outerWidth() / 2;\n                        var handleHeight = $(ui.handle).position().top;\n                        self.component.find('.channel-volume-control-mid').css('top', (handleHeight + offset) + 'px');\n                    }\n                    self.updateVolume();\n                },\n                change: function (event, ui) {\n                    self.updateVolume();\n                    var offset = $(event.target).position().top + $(ui.handle).outerWidth() / 2;\n                    var handleHeight = $(ui.handle).position().top;\n                    self.component.find('.channel-volume-control-mid').css('top', (handleHeight + offset) + 'px');\n                    self.panRight.gain.value = ui.value / 100;\n                    var lv = self.component.find('.left-volume-slider').slider(\"value\");\n                    if (self.unifyVolumeState === true && lv !== ui.value) {\n                        self.panLeft.gain.value = ui.value / 100;\n                        self.component.find('.left-volume-slider').slider(\"value\", ui.value);\n                    }\n                }\n            });\n            channelControlRightContainer.append(rightVolumeSlider);\n            channelVolumeControlContainer.append(channelControlRightContainer);\n            // Add to main widget container\n            this.component.append(channelVolumeSlidersContainer);\n\n        },\n        /**\n         * Add player events listener\n         * @method definePlayerEvents\n         */\n        definePlayerEvents: function () {\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.VOLUME_CHANGE, {\n                self: this\n            }, this.onPlayerVolumeChange);\n        },\n        /**\n         * Set volume value\n         * @method setValue\n         * @param {Object} value\n         */\n        updateVolume: function () {\n            var leftVolume = this.component.find('.left-volume-slider').slider(\"value\");\n            var rightVolume = this.component.find('.right-volume-slider').slider(\"value\");\n            var leftContainer = this.component.find('.channel-volume-info-left span.text');\n            var rightContainer = this.component.find('.channel-volume-info-right span.text');\n            if (leftVolume < 10) {\n                leftContainer.addClass(this.Class.classLeftVolumeStateOff).removeClass(this.Class.classLeftVolumeStateOn);\n            }\n            else {\n                leftContainer.removeClass(this.Class.classLeftVolumeStateOff).addClass(this.Class.classLeftVolumeStateOn);\n            }\n            if (rightVolume < 10) {\n                rightContainer.addClass(this.Class.classRightVolumeStateOff).removeClass(this.Class.classRightVolumeStateOn);\n            }\n            else {\n                rightContainer.removeClass(this.Class.classRightVolumeStateOff).addClass(this.Class.classRightVolumeStateOn);\n            }\n\n            // Main Volume\n            var value = Math.max(leftVolume, rightVolume);\n            var volumeIcon = this.component.find('.volume-control-btn');\n            volumeIcon.attr('class', 'volume-control-btn');\n            if (value < 10) {\n                volumeIcon.addClass(this.Class.classCssVolumeOff);\n            }\n            else if (value < 75) {\n                volumeIcon.addClass(this.Class.classCssVolumeDown);\n            }\n            else {\n                volumeIcon.addClass(this.Class.classCssVolumeOn);\n            }\n\n            if (this.unifyVolumeState === true) {\n                this.component.find('.channel-volume-info-mid .unify').addClass('on').addClass(this.Class.classUnifyVolumeStateOn).removeClass(this.Class.classUnifyVolumeStateOff);\n                this.component.find('.channel-volume-control-mid').addClass('on');\n            }\n            else {\n                this.component.find('.channel-volume-info-mid .unify').removeClass('on').addClass(this.Class.classUnifyVolumeStateOff).removeClass(this.Class.classUnifyVolumeStateOn);\n                this.component.find('.channel-volume-control-mid').removeClass('on');\n            }\n        },\n        /**\n         * Fired on click event\n         * @method onClick\n         * @param {Object} event\n         * @event fr.ina.amalia.player.plugins.controlBar.widgets.VolumeControlBar.event.type.HOVER\n         */\n        onClick: function (event) {\n            event.data.component.trigger(event.data.self.Class.eventTypes.CLICK);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClick/triggerevent:\" + event.data.self.Class.eventTypes.CLICK);\n            }\n        },\n\n        /**\n         * Fired on volume change value\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onPlayerVolumeChange: function (event, data) {\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPlayerVolumeChange\" + parseInt(data.volume));\n            }\n            event.data.self.component.find('.left-volume-slider').slider(\"value\", data.volume);\n            event.data.self.component.find('.right-volume-slider').slider(\"value\", data.volume);\n            event.data.self.updateVolume();\n        },\n        /**\n         * Fired on volume change value\n         * @param {Object} event\n         */\n        onClickUnifyVolumeState: function (event) {\n            if (event.data.self.unifyVolumeState === false) {\n                event.data.self.unifyVolumeState = true;\n                var leftVolume = event.data.self.component.find('.left-volume-slider').slider(\"value\");\n                var rightVolume = event.data.self.component.find('.right-volume-slider').slider(\"value\");\n                event.data.self.component.find('.left-volume-slider').slider(\"value\", Math.max(leftVolume, rightVolume));\n                event.data.self.component.find('.right-volume-slider').slider(\"value\", Math.max(leftVolume, rightVolume));\n            }\n            else {\n                event.data.self.unifyVolumeState = false;\n                event.data.self.updateVolume();\n            }\n\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickUnifyVolumeState/unifyVolumeState:\" + event.data.self.unifyVolumeState);\n            }\n        },\n        /**\n         * Fired on volume change value\n         * @method onClickToVolume\n         * @param {Object} event\n         */\n        onClickToVolume: function (event) {\n            event.data.self.component.find('.channel-volume-sliders').addClass(\"on\");\n        },\n        /**\n         * Fired on volume change value\n         * @method onChannelVolumeMouseout\n         * @param {Object} event\n         */\n        onChannelVolumeMouseout: function (event) {\n            event.data.self.component.find('.channel-volume-sliders').removeClass(\"on\");\n        },\n        /**\n         * Fired on click to left Volume\n         * @method onClickLeftVolume\n         * @param {Object} event\n         */\n        onClickLeftVolume: function (event) {\n            var leftVolume = event.data.self.component.find('.left-volume-slider').slider(\"value\");\n            if (leftVolume < 10) {\n                event.data.self.component.find('.left-volume-slider').slider(\"value\", 100);\n            }\n            else {\n                event.data.self.component.find('.left-volume-slider').slider(\"value\", 0);\n            }\n        },\n        /**\n         * Fired on click to right Volume\n         * @method onClickLeftVolume\n         * @param {Object} event\n         */\n        onClickRightVolume: function (event) {\n            var rightVolume = event.data.self.component.find('.right-volume-slider').slider(\"value\");\n            if (rightVolume < 10) {\n                event.data.self.component.find('.right-volume-slider').slider(\"value\", 100);\n            }\n            else {\n                event.data.self.component.find('.right-volume-slider').slider(\"value\", 0);\n            }\n        }\n\n    });\n"
  },
  {
    "path": "src/player/widgets/fullscreen-button.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Full screen button for control bar\r\n * @class FullscreenButton\r\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\r\n * @module player\r\n * @submodule player-controlbar\r\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\r\n */\r\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.FullscreenButton\", {\r\n        classCss: \"player-fullscreen-button\",\r\n        classCssFullscreenOn: \"ajs-icon ajs-icon-controlbar-fullscreen\",\r\n        classCssFullscreenOff: \"ajs-icon ajs-icon-controlbar-compress\",\r\n        style: \"\",\r\n        eventTypes: {\r\n            CLICK: \"fr.ina.amalia.player.plugins.widgets.FullscreenButton.event.click\"\r\n        }\r\n    },\r\n    {\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            // Create component\r\n            this.component = $('<div>', {\r\n                class: this.Class.classCss,\r\n                style: this.Class.style\r\n            });\r\n            var icon = $('<span>', {\r\n                class: this.Class.classCssFullscreenOn\r\n            });\r\n            this.component.append(icon);\r\n\r\n            // set events\r\n            this.component.on('click', {\r\n                self: this,\r\n                component: this.component\r\n            }, this.onClick);\r\n            // Add to container\r\n            this.container.append(this.component);\r\n            this.setFullscreenMode(false);\r\n            // Logger\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"initialize\");\r\n            }\r\n            this.definePlayerEvents();\r\n        },\r\n        /**\r\n         * Add player evnet listener\r\n         * @method definePlayerEvents\r\n         */\r\n        definePlayerEvents: function () {\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE, {\r\n                self: this\r\n            }, this.onFullscreenModeChange);\r\n        },\r\n        /**\r\n         * Set full-screen mode\r\n         * @method setFullscreenMode\r\n         * @param {Boolean} state true pour le mode plein ecran\r\n         */\r\n        setFullscreenMode: function (state) {\r\n            if (state === true) {\r\n                this.component.find('span').removeClass(this.Class.classCssFullscreenOn).addClass(this.Class.classCssFullscreenOff);\r\n            }\r\n            else {\r\n                this.component.find('span').removeClass(this.Class.classCssFullscreenOff).addClass(this.Class.classCssFullscreenOn);\r\n            }\r\n        },\r\n        /**\r\n         * On click event call toggle full-screen function\r\n         * @method onClick\r\n         * @param {Boolean} state true if in full screen state\r\n         */\r\n        onClick: function (event) {\r\n            event.data.self.mediaPlayer.toggleFullScreen();\r\n        },\r\n        /**\r\n         * Fired on full-screen mode change\r\n         * @method onFullscreenModeChange\r\n         * @param {Object} event\r\n         */\r\n        onFullscreenModeChange: function (event, data) {\r\n            event.data.self.setFullscreenMode(data.inFullScreen);\r\n        }\r\n\r\n    });\r\n"
  },
  {
    "path": "src/player/widgets/jog-shuttle-button.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Seek button widget\r\n * @class JogShuttleButton\r\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\r\n * @module player\r\n * @submodule player-controlbar\r\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\r\n */\r\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.JogShuttleButton\", {\r\n        classCss: \"player-jog-shuttle-button\",\r\n        style: \"\"\r\n    },\r\n    {\r\n        /**\r\n         * Playback speed\r\n         * @property playbackSpeed\r\n         * @type {Object}\r\n         * @default 3\r\n         */\r\n        playbackSpeed: 3,\r\n        /**\r\n         * Playback default speed\r\n         * @property playbackSpeedList\r\n         * @type {Object}\r\n         * @default 3\r\n         */\r\n        playbackDefaultSpeed: 4,\r\n        /**\r\n         * Player instance\r\n         * @property playbackSpeedList\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        playbackSpeedList: [\r\n            -2,\r\n            -1,\r\n            -0.5,\r\n            -0.1,\r\n            1,\r\n            0.5,\r\n            1.5,\r\n            4,\r\n            8\r\n        ],\r\n        forwardSpeedIdx: 0,\r\n        backwardSpeedIdx: 0,\r\n        backwardSpeedList: [\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-0x',\r\n                speed: 1\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-1x',\r\n                speed: -0.1\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-1x',\r\n                speed: -0.25\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-2x',\r\n                speed: -0.5\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-2x',\r\n                speed: -0.75\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-3x',\r\n                speed: -1\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-3x',\r\n                speed: -1.5\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-backward-4x',\r\n                speed: -2\r\n            }\r\n        ],\r\n        forwardSpeedList: [\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-0x',\r\n                speed: 1\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-1x',\r\n                speed: 0.5\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-1x',\r\n                speed: 0.75\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-2x',\r\n                speed: 1.5\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-2x',\r\n                speed: 2\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-3x',\r\n                speed: 4\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-3x',\r\n                speed: 6\r\n            },\r\n            {\r\n                style: 'ajs-icon ajs-icon-jogs-forward-4x',\r\n                speed: 8\r\n            }\r\n        ],\r\n        sliding: false,\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            this.forwardSpeedIdx = 0;\r\n            this.backwardSpeedIdx = 0;\r\n            this.sliding = false;\r\n            // Create component\r\n            this.component = $('<div>', {\r\n                'class': this.Class.classCss,\r\n                'style': this.Class.style\r\n            });\r\n            var buttonContainer = $(\"<span>\", {\r\n                class: \"button-container\"\r\n            });\r\n\r\n            //backward ctrl\r\n            var backwardCtrl = $(\"<div>\", {\r\n                class: \"backward-container ajs-icon ajs-icon-jogs-backward-4x\"\r\n            });\r\n            var iconBackward = $('<span>', {\r\n                class: this.backwardSpeedList[0].style\r\n            });\r\n            backwardCtrl.append(iconBackward);\r\n            backwardCtrl.on('click', {\r\n                self: this\r\n            }, this.onClickToBackward);\r\n            backwardCtrl.on('dblclick', {\r\n                self: this\r\n            }, this.onDblClickToBackward);\r\n\r\n            buttonContainer.append(backwardCtrl);\r\n            // forward ctrl\r\n            var forwardCtrl = $(\"<div>\", {\r\n                class: \"forward-container ajs-icon ajs-icon-jogs-forward-4x\"\r\n            });\r\n            var iconForward = $('<span>', {\r\n                class: this.forwardSpeedList[0].style\r\n            });\r\n            forwardCtrl.append(iconForward);\r\n            forwardCtrl.on('click', {\r\n                self: this\r\n            }, this.onClickToForward);\r\n            forwardCtrl.on('dblclick', {\r\n                self: this\r\n            }, this.onDblClickToForward);\r\n\r\n            buttonContainer.append(forwardCtrl);\r\n\r\n            var jogShuttleSeparator = $(\"<div>\", {\r\n                class: \"jog-shuttle-separator\"\r\n            }).on('mouseover', {\r\n                self: this\r\n            }, this.onHoverJogShuttle);\r\n            buttonContainer.append(jogShuttleSeparator);\r\n\r\n            //jog shuttle container\r\n            var self = this;\r\n            var jogShuttle = $('<div>', {\r\n                class: 'jog-shuttle'\r\n            }).slider({\r\n                min: 0,\r\n                max: 8,\r\n                value: self.playbackDefaultSpeed,\r\n                slide: function (event, ui) {\r\n                    if (self.playbackSpeedList.length >= ui.value) {\r\n                        self.setPlaybackSpeed(parseFloat(self.playbackSpeedList[ui.value]));\r\n                    }\r\n                    self.sliding = true;\r\n                },\r\n                stop: function () {\r\n                    //set default value\r\n                    $(this).slider(\"option\", \"value\", self.playbackDefaultSpeed);\r\n                    self.setPlaybackSpeed(self.playbackDefaultSpeed);\r\n                    self.mediaPlayer.play();\r\n                    self.sliding = false;\r\n                    self.component.find('div.jog-shuttle').removeClass('on');\r\n                }\r\n            });\r\n            jogShuttle.find('.ui-slider-handle').addClass('ajs-icon ajs-icon-jogs-center');\r\n            buttonContainer.append(jogShuttle);\r\n\r\n\r\n            this.component.append(buttonContainer);\r\n\r\n            // Add to container\r\n            this.container.append(this.component);\r\n            jogShuttle.find('.ui-slider-handle').on('mouseleave', {\r\n                self: this\r\n            }, this.onJogShuttleMouseout);\r\n            this.definePlayerEvents();\r\n        },\r\n        /**\r\n         * set player events\r\n         * @returns {undefined}\r\n         */\r\n        definePlayerEvents: function () {\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.PLAYBACK_RATE_CHANGE, {\r\n                self: this\r\n            }, this.onPlaybackRateChange);\r\n        },\r\n        /**\r\n         * In charge to set player playback rate\r\n         * @method setPlaybackSpeed\r\n         * @param playbackSpeed Number\r\n         */\r\n        setPlaybackSpeed: function (playbackSpeed) {\r\n            this.playbackSpeed = parseFloat(playbackSpeed);\r\n            this.mediaPlayer.setPlaybackrate(this.playbackSpeed);\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"playbackRate:\" + this.playbackSpeed);\r\n            }\r\n        },\r\n        /**\r\n         * In charge to update backward and forward controls\r\n         */\r\n        updateControls: function () {\r\n            var playbackSpeed = parseFloat(this.playbackSpeed);\r\n            var forwardSpeed = $.grep(this.forwardSpeedList, function (item) {\r\n                return item.speed === playbackSpeed;\r\n            });\r\n            var forwardSpeedStyle = (forwardSpeed.length <= 0) ? this.forwardSpeedList[0].style : forwardSpeed[0].style;\r\n\r\n            var backwardSpeed = $.grep(this.backwardSpeedList, function (item) {\r\n                return item.speed === playbackSpeed;\r\n            });\r\n            var backwardSpeedStyle = (backwardSpeed.length <= 0) ? this.backwardSpeedList[0].style : backwardSpeed[0].style;\r\n\r\n            this.component.find('.forward-container span:first').attr('class', forwardSpeedStyle);\r\n            this.component.find('.backward-container span:first').attr('class', backwardSpeedStyle);\r\n        },\r\n        /**\r\n         * Triggered to click forward control\r\n         * @param event Object\r\n         */\r\n        onClickToForward: function (event) {\r\n            if (event.data.self.sliding === false) {\r\n                event.data.self.forwardSpeedIdx = (event.data.self.forwardSpeedList.length - 1 > event.data.self.forwardSpeedIdx) ? event.data.self.forwardSpeedIdx + 1 : 0;\r\n                event.data.self.backwardSpeedIdx = 0;\r\n                var forwardSpeed = event.data.self.forwardSpeedList[event.data.self.forwardSpeedIdx].speed;\r\n                event.data.self.setPlaybackSpeed(forwardSpeed);\r\n                if (event.data.self.logger !== null) {\r\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickToForward : \" + event.data.self.forwardSpeedIdx);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Triggered to click backward control\r\n         * @param event Object\r\n         */\r\n        onClickToBackward: function (event) {\r\n            if (event.data.self.sliding === false) {\r\n                event.data.self.forwardSpeedIdx = 0;\r\n                event.data.self.backwardSpeedIdx = (event.data.self.backwardSpeedList.length - 1 > event.data.self.backwardSpeedIdx) ? event.data.self.backwardSpeedIdx + 1 : 0;\r\n                var backwardSpeed = event.data.self.backwardSpeedList[event.data.self.backwardSpeedIdx].speed;\r\n                event.data.self.setPlaybackSpeed(backwardSpeed);\r\n                if (event.data.self.logger !== null) {\r\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickToBackward :\" + event.data.self.backwardSpeedIdx);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Triggered to db click forward control\r\n         * @param event Object\r\n         */\r\n        onDblClickToForward: function (event) {\r\n            event.data.self.forwardSpeedIdx = 0;\r\n            event.data.self.mediaPlayer.setCurrentTime(event.data.self.mediaPlayer.getDuration());\r\n\r\n            var defaultForwardSpeedStyle = event.data.self.forwardSpeedList[0].style;\r\n            event.data.self.component.find('.forward-container span:first').attr('class', 'ajs-icon ajs-icon-jogs-fast-forward').delay(1500).queue(function () {\r\n                event.data.self.component.find('.forward-container span:first').attr('class', defaultForwardSpeedStyle);\r\n            });\r\n\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onDbClickToForward : \" + event.data.self.forwardSpeedIdx);\r\n            }\r\n        },\r\n        /**\r\n         * Triggered to db click backward control\r\n         * @param event Object\r\n         */\r\n        onDblClickToBackward: function (event) {\r\n            event.data.self.backwardSpeedIdx = 0;\r\n            event.data.self.mediaPlayer.setCurrentTime(0);\r\n\r\n            var defaultBackwardSpeedStyle = event.data.self.backwardSpeedList[0].style;\r\n            event.data.self.component.find('.backward-container span:first').attr('class', 'ajs-icon ajs-icon-jogs-fast-backward').delay(1500).queue(function () {\r\n                event.data.self.component.find('.backward-container span:first').attr('class', defaultBackwardSpeedStyle);\r\n            });\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onDbClickToBackward :\" + event.data.self.backwardSpeedIdx);\r\n            }\r\n        },\r\n        /**\r\n         * Triggered to db click backward control\r\n         * @param event Object\r\n         */\r\n        onHoverJogShuttle: function (event) {\r\n            event.data.self.component.find('div.jog-shuttle').addClass('on');\r\n        },\r\n        /**\r\n         * Triggered when mouse out event\r\n         * @param {type} event\r\n         * @returns {undefined}\r\n         */\r\n        onJogShuttleMouseout: function (event) {\r\n            if (event.data.self.sliding === false) {\r\n                event.data.self.component.find('div.jog-shuttle').removeClass('on');\r\n            }\r\n        },\r\n        /**\r\n         * Triggered when playback rate change\r\n         * @returns {undefined}\r\n         */\r\n        onPlaybackRateChange: function (event, data) {\r\n            event.data.self.playbackSpeed = parseFloat(data.rate);\r\n            event.data.self.updateControls();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"playbackRate:\" + event.data.self.playbackSpeed);\r\n            }\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/player/widgets/label.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Label widget\n * @class Label\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\n * @module player\n * @submodule player-controlbar\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\n */\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.Label\", {\n        classCss: \"iplayer-Label label label-default\",\n        style: \"\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Initialize the component\n         * @method initialize\n         */\n        initialize: function () {\n            // Create component\n            this.component = $('<div>', {\n                'class': this.Class.classCss,\n                'style': this.Class.style,\n                text: this.parameter.defaultValue\n            });\n            // Add to container\n            this.container.append(this.component);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n        },\n        /**\n         * Set label text\n         * @method setValue\n         * @param {Object} value\n         */\n        setValue: function (value) {\n            this.component.text(value);\n        }\n    });\n"
  },
  {
    "path": "src/player/widgets/pause-button.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Class en charge du bouton de commande pause.\r\n * @class PauseButton\r\n * @module player\r\n * @submodule player-controlbar\r\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\r\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\r\n */\r\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.PauseButton\", {\r\n        classCss: \"player-pause-button\",\r\n        style: \"\",\r\n        pauseIcon: 'ajs-icon ajs-icon-controlbar-pause'\r\n    },\r\n    {\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            // Create component\r\n            this.component = $('<div>', {\r\n                'class': this.Class.classCss,\r\n                'style': this.Class.style\r\n            });\r\n            var buttonContainer = $(\"<span>\", {\r\n                class: \"button-container\"\r\n            });\r\n            var pauseIcon = $('<i>', {\r\n                class: this.Class.pauseIcon\r\n            });\r\n            buttonContainer.append(pauseIcon);\r\n            this.component.append(buttonContainer);\r\n            // set events\r\n            this.component.on('click', {\r\n                    self: this,\r\n                    component: this.component\r\n                },\r\n                this.onClick);\r\n            // Add to container\r\n            this.container.append(this.component);\r\n            this.definePlayerEvents();\r\n            this.component.addClass('off').removeClass('on');\r\n        },\r\n        /**\r\n         * Add player event listener\r\n         * @method definePlayerEvents\r\n         */\r\n        definePlayerEvents: function () {\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.PLAYING, {\r\n                self: this\r\n            }, this.onPlaying);\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.PAUSED, {\r\n                self: this\r\n            }, this.onPaused);\r\n\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.CAST_PLAYING, {\r\n                self: this\r\n            }, this.onPlaying);\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.CAST_PAUSED, {\r\n                self: this\r\n            }, this.onPaused);\r\n        },\r\n        /**\r\n         * Fired on click event\r\n         * @method onClick\r\n         * @param {Object} event\r\n         */\r\n        onClick: function (event) {\r\n            if (!event.data.self.mediaPlayer.getCastMode()) {\r\n                if (event.data.self.mediaPlayer.isPaused()) {\r\n                    event.data.self.mediaPlayer.play();\r\n                }\r\n                else {\r\n                    event.data.self.mediaPlayer.pause();\r\n                }\r\n            }\r\n            else {\r\n                event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.CAST_PAUSED);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on playing event for show pause button.\r\n         * @method onPlaying\r\n         * @param {Object} event\r\n         */\r\n        onPlaying: function (event) {\r\n            event.data.self.component.addClass('on').removeClass('off');\r\n        },\r\n        /**\r\n         * Fired on paused event for hide pause button.\r\n         * @method onPaused\r\n         * @param {Object} event\r\n         */\r\n        onPaused: function (event) {\r\n            event.data.self.component.addClass('off').removeClass('on');\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/player/widgets/play-button.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Play button widget\r\n * @class PlayButton\r\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\r\n * @module player\r\n * @submodule player-controlbar\r\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\r\n */\r\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.PlayButton\", {\r\n        classCss: \"player-play-button\",\r\n        style: \"\",\r\n        playIcon: 'ajs-icon ajs-icon-controlbar-play'\r\n    },\r\n    {\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            // Create component\r\n            this.component = $('<div>', {\r\n                'class': this.Class.classCss,\r\n                'style': this.Class.style\r\n            });\r\n            var buttonContainer = $(\"<span>\", {\r\n                class: \"button-container\"\r\n            });\r\n            var playIcon = $('<i>', {\r\n                class: this.Class.playIcon\r\n            });\r\n            buttonContainer.append(playIcon);\r\n            this.component.append(buttonContainer);\r\n            // set events\r\n            this.component.on('click', {\r\n                    self: this,\r\n                    component: this.component\r\n                },\r\n                this.onClick);\r\n            // Add to container\r\n            this.container.append(this.component);\r\n            this.definePlayerEvents();\r\n        },\r\n        /**\r\n         * Add player events for play button\r\n         * @method definePlayerEvents\r\n         */\r\n        definePlayerEvents: function () {\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.PLAYING, {\r\n                self: this\r\n            }, this.onPlaying);\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.PAUSED, {\r\n                self: this\r\n            }, this.onPaused);\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.CAST_PLAYING, {\r\n                self: this\r\n            }, this.onPlaying);\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.CAST_PAUSED, {\r\n                self: this\r\n            }, this.onPaused);\r\n        },\r\n        /**\r\n         * Fired on click event\r\n         * @method onClick\r\n         * @param {Object} event\r\n         */\r\n        onClick: function (event) {\r\n            if (!event.data.self.mediaPlayer.getCastMode()) {\r\n                event.data.self.mediaPlayer.play();\r\n            }\r\n            else {\r\n                event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.CAST_PLAYING);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on playing event for hide play button.\r\n         * @method onPlaying\r\n         * @param {Object} event\r\n         */\r\n        onPlaying: function (event) {\r\n            event.data.self.component.addClass('off').removeClass('on');\r\n        },\r\n        /**\r\n         * Fired on paused event for show play button.\r\n         * @method onPaused\r\n         * @param {Object} event\r\n         */\r\n        onPaused: function (event) {\r\n            event.data.self.component.addClass('on').removeClass('off');\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/player/widgets/progress-bar.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * Progress bar widget\r\n * @class ProgressBar\r\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\r\n * @module player\r\n * @submodule player-controlbar\r\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\r\n */\r\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.ProgressBar\", {\r\n        classCss: \"player-progress-bar\",\r\n        style: \"\"\r\n    },\r\n    {\r\n        /**\r\n         * Slider start value\r\n         * @property sliderStart\r\n         * @type {Number}\r\n         * @default 0\r\n         */\r\n        sliderStart: 0,\r\n        /**\r\n         * Slider end value\r\n         * @property sliderEnd\r\n         * @type {Object}\r\n         * @default 1000\r\n         */\r\n        sliderEnd: 1000,\r\n        /**\r\n         * Slider state, true in sliding mode\r\n         * @property sliding\r\n         * @type {Boolean}\r\n         * @default false\r\n         */\r\n        sliding: false,\r\n        /**\r\n         * Defines configuration\r\n         * @property defaultValue\r\n         * @type {Number}\r\n         * @default 0\r\n         */\r\n        defaultValue: 0,\r\n        /**\r\n         * Last slide event\r\n         * @property lastSlideEvent\r\n         * @type {Number}\r\n         * @default 0\r\n         */\r\n        lastSlideEvent: 0,\r\n        /**\r\n         * Last playback state, true for playing state\r\n         * @property lastPlaybackState\r\n         * @type {Boolean}\r\n         * @default 0\r\n         */\r\n        lastPlaybackState: false,\r\n\r\n        /**\r\n         * Initialize the component\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            this.lastPlaybackState = false;\r\n            // Create component\r\n            this.component = $('<div>', {\r\n                'class': this.Class.classCss,\r\n                'style': this.Class.style\r\n            });\r\n            // ui bootstrap\r\n            this.component.slider({\r\n                range: \"min\",\r\n                min: this.sliderStart,\r\n                max: this.sliderEnd,\r\n                value: this.defaultValue,\r\n                height: 10,\r\n                framepreview: true,\r\n                framepreviewTimeBound: 500\r\n            });\r\n            this.component.prepend($('<div>', {\r\n                class: 'buffer-bar'\r\n            }));\r\n            // set events\r\n            this.component.on('slidestart', {\r\n                self: this,\r\n                component: this.component\r\n            }, this.onSlideStart);\r\n            this.component.on('slidestop', {\r\n                self: this,\r\n                component: this.component\r\n            }, this.onSlideStop);\r\n\r\n            // Add event on slide\r\n            this.component.on('slide', {\r\n                self: this,\r\n                component: this.component\r\n            }, this.onSlide);\r\n\r\n            // Add to container\r\n            this.container.append(this.component);\r\n            this.definePlayerEvents();\r\n        },\r\n        /**\r\n         * Set progress bar value.\r\n         * @method setValue\r\n         * @param {Object} value\r\n         */\r\n        setValue: function (value) {\r\n            if (this.sliding === false) {\r\n                this.component.slider(\"value\", value * 10);\r\n            }\r\n        },\r\n        /**\r\n         * Set progress bar title\r\n         * @method setTitle\r\n         * @param {Object} value\r\n         */\r\n        setTitle: function (value) {\r\n            this.component.attr(\"title\", value);\r\n        },\r\n        /**\r\n         * Update buffer component\r\n         * @method updateBuffer\r\n         * @param {Object} bufferOffset\r\n         * @param {Object} bufferWidth\r\n         */\r\n        updateBuffer: function (bufferOffset, bufferWidth) {\r\n            this.component.find('.buffer-bar').css({\r\n                \"left\": bufferOffset + '%',\r\n                'width': bufferWidth + '%'\r\n            });\r\n        },\r\n        /**\r\n         * Add player events listener\r\n         * @method definePlayerEvents\r\n         */\r\n        definePlayerEvents: function () {\r\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\r\n                self: this\r\n            }, this.onTimechange);\r\n        },\r\n        /**\r\n         * Fired on time change event for update progress bar position\r\n         * @method onTimechange\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onTimechange: function (event, data) {\r\n            var currentTime = fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.currentTime, event.data.self.settings.framerate, event.data.self.settings.timeFormat);\r\n            // Set progress bar position\r\n            event.data.self.setValue(data.percentage);\r\n            // Set tooltip time\r\n            event.data.self.setTitle(currentTime);\r\n            if (event.data.self.mediaPlayer.getMediaPlayer() !== null && typeof event.data.self.mediaPlayer.getMediaPlayer().get(0).buffered !== \"undefined\" && event.data.self.mediaPlayer.getMediaPlayer().get(0).buffered.length > 0) {\r\n                // The buffered regions of the video\r\n                var currentBuffer = event.data.self.mediaPlayer.getMediaPlayer().get(0).buffered.end(Math.max(0, event.data.self.mediaPlayer.getMediaPlayer().get(0).buffered.length - 1));\r\n                var bufferPercentage = 100 * currentBuffer / (data.duration - data.tcOffset);\r\n                event.data.self.updateBuffer(0, bufferPercentage);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on slide start\r\n         * @method onSlideStart\r\n         * @param {Object} event\r\n         * @param {Object} ui\r\n         */\r\n        onSlideStart: function (event, ui) {\r\n            event.data.self.sliding = true;\r\n            event.data.self.lastPlaybackState = !event.data.self.mediaPlayer.isPaused();\r\n            event.data.self.mediaPlayer.pause();\r\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.START_SEEKING, {\r\n                percentage: ui.value / 10\r\n            });\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSlideStart Value : \" + ui.value);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on slide end\r\n         * @method onSlideStop\r\n         * @param {Object} event\r\n         * @param {Object} ui\r\n         */\r\n        onSlideStop: function (event, ui) {\r\n            event.data.self.sliding = false;\r\n            var duration = event.data.self.mediaPlayer.getDuration();\r\n            var percentage = ui.value / 10;\r\n            var tc = (duration * percentage) / 100;\r\n            event.data.self.mediaPlayer.setCurrentTime(tc + event.data.self.mediaPlayer.getTcOffset());\r\n            if (event.data.self.lastPlaybackState === true) {\r\n                event.data.self.mediaPlayer.play();\r\n            }\r\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.STOP_SEEKING, {\r\n                percentage: ui.value / 10\r\n            });\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSlideStop value : \" + ui.value);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on slide event\r\n         * @method onSlide\r\n         * @param {Object} event\r\n         * @param {Object} ui\r\n         */\r\n        onSlide: function (event, ui) {\r\n\r\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.SEEKING, {\r\n                percentage: ui.value / 10\r\n            });\r\n            // Only if framepreview is enabled\r\n            if (event.data.self.parameter.framepreview === true) {\r\n                var slideEvent = new Date();\r\n                var diff = (slideEvent - event.data.self.lastSlideEvent);\r\n                if (diff > event.data.self.parameter.framepreviewTimeBound) {\r\n                    event.data.self.lastSlideEvent = new Date();\r\n                    var duration = event.data.self.mediaPlayer.getDuration();\r\n                    var percentage = ui.value / 10;\r\n                    var tc = (duration * percentage) / 100;\r\n                    event.data.self.mediaPlayer.setCurrentTime(tc + event.data.self.mediaPlayer.getTcOffset());\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.trace(event.data.self.Class.fullName, \"onSlide value : \" + ui.value);\r\n                    }\r\n                }\r\n            }\r\n\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/player/widgets/time-label.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Time label widget\n * @class TimeLabel\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\n * @module player\n * @submodule player-controlbar\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\n */\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.TimeLabel\", {\n        classCss: \"player-timelabel time-display off\",\n        style: \"\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Current position (in seconds) of the audio/video\n         * @property currentTime\n         * @type {Object}\n         * @default null\n         */\n        currentTime: null,\n        /**\n         * time seperator element\n         * @property timeSeparator\n         * @type {Object}\n         * @default null\n         */\n        timeSeparator: null,\n        /**\n         * Media duration in seconds\n         * @property duration\n         * @type {Object}\n         * @default null\n         */\n        duration: null,\n        /**\n         * Initialize the component\n         * @method initialize\n         */\n        initialize: function () {\n            // Create component\n            this.component = $('<div>', {\n                'class': this.Class.classCss,\n                'style': this.Class.style\n            });\n            this.currentTime = $('<span>', {\n                'class': 'time-current'\n            });\n            this.timeSeparator = $('<span>', {\n                'class': 'time-separator'\n            });\n            this.duration = $('<span>', {\n                'class': 'time-duration'\n            });\n            this.component.append(this.currentTime);\n            this.component.append(this.timeSeparator);\n            this.component.append(this.duration);\n\n            // Add to container\n            this.container.append(this.component);\n            this.definePlayerEvents();\n        },\n        /**\n         * Set current time\n         * @method setCurrentTime\n         * @param {Object} value\n         */\n        setCurrentTime: function (value) {\n            if (this.currentTime !== null) {\n                this.currentTime.text(value);\n            }\n        },\n        /**\n         * Set media duration\n         * @method setDuration\n         * @param {Object} value\n         */\n        setDuration: function (value) {\n            if (this.duration !== null) {\n                this.duration.text(value);\n                this.component.removeClass('off').addClass('on');\n            }\n        },\n        /**\n         * Add player events listener\n         * @method definePlayerEvents\n         */\n        definePlayerEvents: function () {\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this\n            }, this.onTimechange);\n        },\n        /**\n         * Fired on time change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onTimechange: function (event, data) {\n            var currentTime = fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.currentTime, event.data.self.parameter.framerate, event.data.self.parameter.timeFormat);\n            var duration = fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.duration, event.data.self.parameter.framerate, event.data.self.parameter.timeFormat);\n            // set current time\n            event.data.self.setCurrentTime(currentTime);\n            event.data.self.setDuration(duration);\n        }\n    });\n"
  },
  {
    "path": "src/player/widgets/volume-control-bar.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Volume control bar widget\n * @class VolumeControlBar\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\n * @module player\n * @submodule player-controlbar\n * @extends fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\n */\nfr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase.extend(\"fr.ina.amalia.player.plugins.controlBar.widgets.VolumeControlBar\", {\n        classCss: \"player-volume-control\",\n        classCssVolumeOn: \"ajs-icon ajs-icon-controlbar-volume_max\",\n        classCssVolumeUp: \"ajs-icon ajs-icon-controlbar-volume_max\",\n        classCssVolumeDown: \"ajs-icon ajs-icon-controlbar-volume-min\",\n        classCssVolumeOff: \"ajs-icon ajs-icon-controlbar-volume-off\",\n        style: \"\",\n        eventTypes: {\n            CLICK: \"fr.ina.amalia.player.plugins.widgets.VolumeControlBar.event.click\",\n            CHANGE: \"fr.ina.amalia.player.plugins.widgets.VolumeControlBar.event.change\",\n            HOVER: \"fr.ina.amalia.player.plugins.widgets.VolumeControlBar.event.hover\"\n        }\n    },\n    {\n        /**\n         * Slide start value\n         * @property sliderStart\n         * @type {Object}\n         * @default 0\n         */\n        sliderStart: 0,\n        /**\n         * Slide end value\n         * @property sliderEnd\n         * @type {Object}\n         * @default 100\n         */\n        sliderEnd: 100,\n        /**\n         * Default value\n         * @property defaultValue\n         * @type {Object}\n         * @default 100\n         */\n        defaultValue: 100,\n        /**\n         * Volume component element\n         * @property volumeControlComponent\n         * @type {Object}\n         * @default null\n         */\n        volumeControlComponent: null,\n        /**\n         * Volume slider component\n         * @property volumeSliderComponent\n         * @type {Object}\n         * @default null\n         */\n        volumeSliderComponent: null,\n        /**\n         * Initialize the component\n         * @method initialize\n         */\n        initialize: function () {\n            this.volumeSliderComponent = null;\n            // Create component\n            this.component = $('<div>', {\n                'class': this.Class.classCss,\n                'style': this.Class.style\n            });\n            var volumeElement = '<input class=\"volume-control\" data-width=\"70\" data-height=\"70\" data-displayInput=\"false\" data-displayPrevious=\"true\" data-skin=\"tron\" data-thickness=\".2\" data-fgColor=\"#13e7f0\"value=\"' + this.defaultValue + '\" >';\n            this.component.append(volumeElement);\n            var volumeControleElement = $('<div>', {\n                class: 'volume-control-btn'\n            });\n\n            var volumeControleBtn = $('<span>');\n            volumeControleElement.append(volumeControleBtn);\n            // self object for knob events\n            var self = this;\n            this.component.find('input.volume-control').knob({\n                change: function (v) {\n                    self.onSlide(v);\n                }\n            });\n            //Set events\n            volumeControleElement.on('mouseover', {\n                self: this\n            }, this.onMouseover);\n            volumeControleElement.find('span').on('click', {\n                self: this\n            }, this.onClickVolume);\n\n            this.component.find('div').append(volumeControleElement);\n            // Slider\n            var sliderElement = $('<div>', {\n                'class': 'volume-slider-ctn off'\n            });\n            this.volumeSliderComponent = $('<div>', {\n                'class': 'slider-volume'\n            });\n            sliderElement.append(this.volumeSliderComponent);\n            volumeControleElement.append(sliderElement);\n            this.volumeSliderComponent.slider({\n                orientation: \"vertical\",\n                min: 0,\n                max: 100,\n                range: \"min\",\n\n                value: this.mediaPlayer.getVolume(),\n                slide: function (event, ui) {\n                    self.onSlide(ui.value);\n                }\n            });\n\n            sliderElement.on('mouseleave', {\n                self: this\n            }, this.onMouseleave);\n\n            // Add to container\n            this.container.append(this.component);\n            this.definePlayerEvents();\n            this.setValue(this.mediaPlayer.getVolume());\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n        },\n        /**\n         * Add player events listener\n         * @method definePlayerEvents\n         */\n        definePlayerEvents: function () {\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.VOLUME_CHANGE, {\n                self: this\n            }, this.onPlayerVolumeChange);\n        },\n        /**\n         * Fired on click event\n         * @method onClick\n         * @param {Object} event\n         * @event fr.ina.amalia.player.plugins.controlBar.widgets.VolumeControlBar.event.type.HOVER\n         */\n        onClick: function (event) {\n            event.data.component.trigger(event.data.self.Class.eventTypes.CLICK);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClick/triggerevent:\" + event.data.self.Class.eventTypes.CLICK);\n            }\n        },\n        /**\n         * Set volume value\n         * @method setValue\n         * @param {Object} value\n         */\n        setValue: function (value) {\n            this.component.find('input.volume-control').val(value).trigger('change');\n            var volumeIcon = this.component.find('.volume-control-btn span:first');\n            volumeIcon.attr('class', '');\n            if (value === 0) {\n                volumeIcon.addClass(this.Class.classCssVolumeOff);\n            }\n            else if (value < 50) {\n                volumeIcon.addClass(this.Class.classCssVolumeDown);\n            }\n            else if (value >= 50 && value < 75) {\n                volumeIcon.addClass(this.Class.classCssVolumeUp);\n            }\n            else if (value >= 75) {\n                volumeIcon.addClass(this.Class.classCssVolumeOn);\n            }\n        },\n        /**\n         * Fired on slider change value\n         * @method onSlide\n         * @param {Object} value\n         * @event fr.ina.amalia.player.plugins.controlBar.widgets.VolumeControlBar.event.type.CHANGE\n         */\n        onSlide: function (value) {\n            this.component.trigger(this.Class.eventTypes.CHANGE, {\n                'value': value\n            });\n            this.mediaPlayer.setVolume(value);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onSlide/triggerevent:\" + this.Class.eventTypes.CHANGE + \" Value \" + value);\n            }\n        },\n        /**\n         * Fired on click at volume button\n         * @method onClickVolume\n         * @param {Object} event\n         * @event fr.ina.amalia.player.plugins.controlBar.widgets.VolumeControlBar.event.type.CHANGE\n         */\n        onClickVolume: function (event) {\n            var value = event.data.self.mediaPlayer.getVolume() > 50 ? 0 : 100;\n            event.data.self.mediaPlayer.setVolume(value);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickVolume:\" + event.data.self.Class.eventTypes.CHANGE + \" Value \" + value);\n            }\n        },\n        /**\n         * Fired on volume change value\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onPlayerVolumeChange: function (event, data) {\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onPlayerVolumeChange\" + parseInt(data.volume));\n            }\n            event.data.self.setValue(parseInt(data.volume));\n            event.data.self.volumeSliderComponent.slider('value', parseInt(data.volume));\n        },\n        /**\n         * Fired on mouse over\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onMouseover: function (event) {\n            event.data.self.component.find('.volume-slider-ctn').addClass('on').removeClass('off');\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onMouseover\");\n            }\n        },\n\n        /**\n         * Fired on mouse leave\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onMouseleave: function (event) {\n            event.data.self.component.find('.volume-slider-ctn').addClass('off').removeClass('on');\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onMouseover\");\n            }\n        }\n    });\n"
  },
  {
    "path": "src/player/widgets/widget-base.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class for widget\n * @class WidgetBase\n * @namespace fr.ina.amalia.player.plugins.controlBar.widgets\n * @module player\n * @submodule player-controlbar\n * @param {Object} parameter configuration\n * @param {Object} mediaPlayer player instance\n * @param {Object} container main container\n */\n$.Class(\"fr.ina.amalia.player.plugins.controlBar.widgets.WidgetBase\", {}, {\n    /**\n     * Defines configuration\n     * @property settings\n     * @type {Object}\n     * @default {}\n     */\n    settings: {},\n    /**\n     * In charge to render messages in the web console output\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    logger: null,\n    /**\n     * Widget container element\n     * @property element\n     * @type {Object}\n     * @default null\n     */\n    element: null,\n    /**\n     * Component container element\n     * @property component\n     * @type {Object}\n     * @default null\n     */\n    component: null,\n    /**\n     * Player instance\n     * @property mediaPlayer\n     * @type {Object}\n     * @default null\n     */\n    mediaPlayer: null,\n    /**\n     * Init this class\n     * @constructor\n     * @method init\n     * @param {Object} parameter configuration\n     * @param {Object} mediaPlayer player instance\n     * @param {Object} container main container\n     */\n    init: function (parameter, mediaPlayer, container) {\n        this.parameter = $.extend({\n                debug: true\n            },\n            parameter || {});\n\n        if (fr.ina.amalia.player.log !== undefined && fr.ina.amalia.player.log.LogHandler !== undefined) {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.parameter.debug\n            });\n        }\n        this.container = container;\n        this.mediaPlayer = mediaPlayer;\n        this.initialize();\n    },\n    /**\n     * Initialize the component\n     * @method initialize\n     */\n    initialize: function () {\n\n    },\n    /**\n     * Show this widget\n     * @method show\n     */\n    show: function () {\n        this.component.show();\n    },\n    /**\n     * Hide this widget\n     * @method hide\n     */\n    hide: function () {\n        this.component.hide();\n    }\n});\n"
  },
  {
    "path": "src/player-map.js",
    "content": ""
  },
  {
    "path": "src/plugins/captions/captions.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * In charge to caption plugin and display sub title on the player\r\n * @class CaptionsPlugin\r\n * @namespace fr.ina.amalia.player.plugins\r\n * @module plugin\r\n * @submodule plugin-captions\r\n * @constructor\r\n * @extends fr.ina.amalia.player.plugins.captions.CaptionsBase\r\n * @param {Object} settings\r\n * @param {Object} container\r\n */\r\nfr.ina.amalia.player.plugins.captions.CaptionsBase.extend(\"fr.ina.amalia.player.plugins.CaptionsPlugin\", {\r\n        classCss: \"ajs-plugin plugin-caption\",\r\n        classCssFullscreenOn: \"fullScreenOn\",\r\n        classCssFullscreenOff: \"fullScreenOff\"\r\n    },\r\n    {\r\n        /**\r\n         * true if load data started anw\r\n         * @property loadDataStarted\r\n         * @default null\r\n         */\r\n        loadDataStarted: false,\r\n        /**\r\n         * Selected metadata id provide by player core.\r\n         * @property loadDataStarted\r\n         * @default ''\r\n         */\r\n        selectedMetadataId: '',\r\n        /**\r\n         * In charge to create container for display subtitle\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            this.container = $('<div>', {\r\n                class: this.Class.classCss\r\n            });\r\n            this.container.addClass(this.Class.classCssFullscreenOff);\r\n            this.listOfData = [];\r\n            this.settings = $.extend({\r\n                    debug: this.settings.debug,\r\n                    framerate: '25',\r\n                    internalPlugin: false,\r\n                    metadataId: '',\r\n                    level: 2\r\n                },\r\n                this.settings.parameters || {});\r\n            this.pluginContainer.append(this.container);\r\n            this.definePlayerListeners();\r\n        },\r\n        /**\r\n         * Set events listener\r\n         * @method definePlayerListeners\r\n         */\r\n        definePlayerListeners: function () {\r\n            var mainContainer = this.mediaPlayer.getContainer();\r\n            if (this.settings.metadataId === '') {\r\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\r\n                    self: this\r\n                }, this.onSelectedMetadataChange);\r\n            }\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE, {\r\n                self: this\r\n            }, this.onBeginDataChange);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\r\n                self: this\r\n            }, this.onDataChange);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE, {\r\n                self: this\r\n            }, this.onEndDataChange);\r\n\r\n            // Player events\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\r\n                self: this\r\n            }, this.onTimeupdate);\r\n            // Player full screen events\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE, {\r\n                self: this\r\n            }, this.onFullscreenModeChange);\r\n\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\r\n            }\r\n        },\r\n        /**\r\n         * In charge to update block data\r\n         */\r\n        updataBlockData: function () {\r\n            // use settings metadata if not empty\r\n            if (this.settings.metadataId !== '') {\r\n                this.updateMetadata(this.settings.metadataId, this.settings.level, 0, this.mediaPlayer.getDuration());\r\n            }\r\n            else if (this.selectedMetadataId !== '') {\r\n                this.updateMetadata(this.selectedMetadataId, this.settings.level, 0, this.mediaPlayer.getDuration());\r\n            }\r\n        },\r\n        /**\r\n         * In charge to update text with current time\r\n         * @method updatePos\r\n         * @param {Object} currentTime\r\n         */\r\n        updatePos: function (currentTime) {\r\n            currentTime = parseFloat(currentTime);\r\n            var displayData = $.grep(this.listOfData, function (n) {\r\n                return (currentTime >= parseFloat(n.tcin) && currentTime < parseFloat(n.tcout));\r\n            });\r\n            var text = \"\";\r\n            var textData = null;\r\n            for (var i = 0;\r\n                 i < displayData.length;\r\n                 i++) {\r\n                textData = displayData[i];\r\n                if (textData !== null && typeof textData !== \"undefined\" && textData.hasOwnProperty('text') === true) {\r\n                    text += textData.text.toString();\r\n                }\r\n            }\r\n            if (text !== \"\") {\r\n                this.container.html(text);\r\n                this.container.show();\r\n            }\r\n            else {\r\n                this.container.html(\"\");\r\n                this.container.hide();\r\n            }\r\n        },\r\n        /**\r\n         * Set full-screen mode\r\n         * @method setFullscreenMode\r\n         * @param {Boolean} state true pour le mode plein ecran\r\n         */\r\n        setFullscreenMode: function (state) {\r\n            if (state === true) {\r\n                this.container.removeClass(this.Class.classCssFullscreenOff).addClass(this.Class.classCssFullscreenOn);\r\n            }\r\n            else {\r\n                this.container.removeClass(this.Class.classCssFullscreenOn).addClass(this.Class.classCssFullscreenOff);\r\n\r\n            }\r\n        },\r\n        /**\r\n         * Fired on full-screen mode change\r\n         * @method onFullscreenModeChange\r\n         * @param {Object} event\r\n         */\r\n        onFullscreenModeChange: function (event, data) {\r\n            event.data.self.setFullscreenMode(data.inFullScreen);\r\n        },\r\n        /**\r\n         * Fired on selected metadata change\r\n         * @method onSelectedMetadataChange\r\n         */\r\n        onSelectedMetadataChange: function (event, data) {\r\n            if (data.metadataId !== null) {\r\n                event.data.self.selectedMetadataId = data.metadataId.toString();\r\n                event.data.self.updataBlockData();\r\n            }\r\n        },\r\n        /**\r\n         * Fired on begin data change event\r\n         * @method onBeginDataChange\r\n         * @param {Object} event\r\n         */\r\n        onBeginDataChange: function (event) {\r\n            event.data.self.loadDataStarted = true;\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onBeginDataChange\");\r\n            }\r\n        },\r\n        /**\r\n         * Fired on data change event\r\n         * @param {Object} event\r\n         */\r\n        onDataChange: function (event, data) {\r\n            if (event.data.self.loadDataStarted === false && event.data.self.selectedMetadataId === data.id) {\r\n                event.data.self.updataBlockData();\r\n            }\r\n        },\r\n        /**\r\n         * Fired on end data change event\r\n         * @method onEndDataChange\r\n         * @param {Object} event\r\n         */\r\n        onEndDataChange: function (event) {\r\n            event.data.self.loadDataStarted = false;\r\n            event.data.self.updataBlockData();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"EndDataChange\");\r\n            }\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/plugins/control-bar/progress-bar.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to progress bar plugin\n * @class ProgressBarPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-watermark\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.ProgressBarPlugin\", {\n        classCss: \"ajs-plugin plugin-progress-bar\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * progressBar container\n         * @property progressBar\n         * @type {Object}\n         * @default []\n         */\n        progressBar: null,\n        /**\n         * progressBar container\n         * @property sliding\n         * @type {Object}\n         * @default []\n         */\n        sliding: false,\n        /**\n         * Slider start value\n         * @property sliderStart\n         * @type {Number}\n         * @default 0\n         */\n        sliderStart: 0,\n        /**\n         * Slider end value\n         * @property sliderEnd\n         * @type {Object}\n         * @default 1000\n         */\n        sliderEnd: 1000,\n        /**\n         * Defines configuration\n         * @property defaultValue\n         * @type {Number}\n         * @default 0\n         */\n        defaultValue: 0,\n        /**\n         * Initialize watermark plugin and create container of this plugin\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.sliding = false;\n            this.settings = $.extend({\n                framerate: '25',\n                timeFormat: 'f'\n            }, this.settings.parameters || {});\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.pluginContainer.append(this.container);\n            this.createProgressBarElement();\n            this.defineListeners();\n\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            this.mediaPlayer.getContainer().on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this\n            }, this.onTimechange);\n\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * In charge to create watermark element\n         * @method createWatermarkElement\n         */\n        createProgressBarElement: function () {\n            this.timeIndicatorContainer = $('<div>', {\n                class: 'ajs-time-indicator'\n            });\n            var timeIndicator = $('<span>', {\n                class: 'ajs-tooltip-text'\n            });\n            this.timeIndicatorContainer.append(timeIndicator);\n            this.container.append(this.timeIndicatorContainer);\n            this.progressBar = $('<div>', {\n                'class': 'ajs-progress-bar'\n            });\n            this.progressBar.slider({\n                range: \"min\",\n                min: this.sliderStart,\n                max: this.sliderEnd,\n                value: this.defaultValue,\n                height: 20\n            });\n            this.progressBar.prepend($('<div>', {\n                class: 'buffer-bar'\n            }));\n            // set events\n            this.progressBar.on('slidestart', {\n                self: this,\n                component: this.component\n            }, this.onSlideStart);\n            // Add event on slide\n            this.progressBar.on('slide', {\n                self: this,\n                component: this.component\n            }, this.onSlide);\n            this.progressBar.on('slidestop', {\n                self: this,\n                component: this.component\n            }, this.onSlideStop);\n            this.container.append(this.progressBar);\n            //Set events\n            this.container.find('.ajs-progress-bar').on('mouseover', $.proxy(this.onProgressBarMouseover, this));\n            this.container.find('.ajs-progress-bar').on('mouseout', $.proxy(this.onProgressBarMouseoout, this));\n            this.container.find('.ajs-progress-bar').on('mousemove', $.proxy(this.onProgressBarMousemove, this));\n        },\n        /**\n         * Set progress bar value.\n         * @method setValue\n         * @param {Object} value\n         */\n        setValue: function (value) {\n            if (this.sliding === false) {\n                this.progressBar.slider(\"value\", value * 10);\n            }\n        },\n        /**\n         * Set progress bar title\n         * @method setTitle\n         * @param {Object} value\n         */\n        setTitle: function (value) {\n            this.progressBar.attr(\"title\", value);\n        },\n        /** Player Events * */\n        /**\n         * Fired when plugin ready event\n         * @method onTimechange\n         * @param {Object} event\n         */\n        onTimechange: function (event, data) {\n            var currentTime = fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.currentTime, event.data.self.settings.framerate, event.data.self.settings.timeFormat);\n            // Set progress bar position\n            event.data.self.setValue(data.percentage);\n            // Set tooltip time\n            event.data.self.setTitle(currentTime);\n\n        },\n        /**\n         * Fired on slide start\n         * @method onSlideStart\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onSlideStart: function (event, ui) {\n            event.data.self.sliding = true;\n            event.data.self.mediaPlayer.pause();\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.START_SEEKING, {\n                percentage: ui.value / 10\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSlideStart Value : \" + ui.value);\n            }\n        },\n        /**\n         * Fired on slide event\n         * @method onSlide\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onSlide: function (event, ui) {\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.SEEKING, {\n                percentage: ui.value / 10\n            });\n        },\n        /**\n         * Fired on slide end\n         * @method onSlideStop\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onSlideStop: function (event, ui) {\n            event.data.self.sliding = false;\n            var duration = event.data.self.mediaPlayer.getDuration();\n            var percentage = ui.value / 10;\n            var tc = (duration * percentage) / 100;\n            event.data.self.mediaPlayer.setCurrentTime(tc + event.data.self.mediaPlayer.getTcOffset());\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.STOP_SEEKING, {\n                percentage: ui.value / 10\n            });\n            event.data.self.mediaPlayer.play();\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSlideStop value : \" + ui.value);\n            }\n        },\n        /**\n         * Fired on mouse over in progress bar\n         * @method onTimeChange\n         * @param {Object} event\n         */\n        onProgressBarMouseover: function () {\n            this.timeIndicatorContainer.show();\n        },\n        /**\n         * Fired on mouseout in progress bar\n         * @method onTimeChange\n         * @param {Object} event\n         */\n        onProgressBarMouseoout: function () {\n            this.timeIndicatorContainer.hide();\n        },\n        /**\n         * Fired on mouse move in progress bar\n         * @method onTimeChange\n         * @param {Object} event\n         */\n        onProgressBarMousemove: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tooltipMid = this.timeIndicatorContainer.width() / 2;\n            var mPos = event.clientX - currentTarget.offset().left;\n            var leftPos = Math.max(tooltipMid, Math.min(mPos, currentTarget.width() - tooltipMid));\n            var tc = ((mPos * this.mediaPlayer.getDuration()) / currentTarget.width()) + this.mediaPlayer.getTcOffset();\n            this.timeIndicatorContainer.css('left', leftPos);\n            this.timeIndicatorContainer.find('.ajs-tooltip-text').html(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc, this.settings.framerate, this.settings.timeFormat));\n        }\n    });\n"
  },
  {
    "path": "src/plugins/d3js-chart/plugin-d3js-chart.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage annotations\n * @class AnnotationPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-annotation\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBaseMultiBlocks.extend(\"fr.ina.amalia.player.plugins.D3JsChartPlugin\", {\n        classCss: \"ajs-plugin plugin-db3-js-chart\",\n        COMPONENT_NAME: \"focus-component\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * true if load data started anw\n         * @property loadDataStarted\n         * @default null\n         */\n        loadDataStarted: false,\n        /**\n         * List of id to deal\n         * @property loadDataStarted\n         * @default null\n         */\n        dataToDeal: null,\n\n        /**\n         * List of id to deal\n         * @property loadDataStarted\n         * @default null\n         */\n        selectedItem: null,\n        /**\n         * List of id to deal\n         * @property loadDataStarted\n         * @default null\n         */\n        chart: null,\n        /**\n         * Tc in\n         * @property tcin\n         * @default 0\n         */\n        tcin: 0,\n        /**\n         * Tc out\n         * @property tcout\n         * @default 0\n         */\n        tcout: 0,\n\n        /**\n         * Initialize watermark plugin and create container of this plugin\n         * @method initialize\n         */\n        initialize: function () {\n            this.loadDataStarted = true;\n            this.selectedItem = null;\n            this.dataToDeal = [];\n            this.chart = null;\n            this.tcin = 0;\n            this.tcout = 0;\n            this._super();\n            this.settings = $.extend({\n                defaultColor: \"#00CCCC\",\n                rightClickAction: null,\n                framerate: '25',\n                timeFormat: 'f',\n                timecursor: true,\n                colors: [\n                    \"#0d8bc7\", // Secondaire\n                    \"#00CC99\", // Primaire\n                    \"#AE4CFF\",\n                    \"#00FFDA\",\n                    \"#FF654C\",\n                    \"#18B8FF\",\n                    \"#FF38A0\",\n                    \"#A9FF78\",\n                    \"#FF9500\",\n                    \"#00C7FF\",\n                    \"#FF1986\",\n                    \"#D7FF19\",\n                    \"#FFD907\",\n                    \"#4CFFEE\",\n                    \"#FF00AD\",\n                    \"#82FF4C\"\n                ],\n                withFocus: false,\n                callbacks: [\n                    {\n                        label: 'voir',\n                        callback: ''\n                    }\n                ]\n            }, this.settings.parameters || {});\n\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.pluginContainer.append(this.container);\n            // Set default data type managed by this plugin.\n            this.registerMetadataType();\n            // Set Player events\n            this.definePlayerListeners();\n            this.loaderContainer.show();\n        },\n        /**\n         * In charge to register data type managed by this plugin.\n         * @method registerMetadataType\n         */\n        registerMetadataType: function () {\n            this.listOfMetadataTypes = [];\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.HISTOGRAM);\n        },\n\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        definePlayerListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            // Player events\n            mainContainer.one(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, $.proxy(this.onPluginReady, this));\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE, $.proxy(this.onBeginDataChange, this));\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE, $.proxy(this.onEndDataChange, this));\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, $.proxy(this.onDataChange, this));\n            if (this.settings.timecursor === true) {\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, $.proxy(this.onTimeupdate, this));\n            }\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"DefinePlayerListeners\");\n            }\n        },\n        /**\n         * Initialize plugin on load start\n         * @method initializeOnLoadStart\n         * @returns {undefined}\n         */\n        initializeOnLoadStart: function () {\n            this.tcin = this.mediaPlayer.getTcOffset();\n            this.tcout = this.tcin + this.mediaPlayer.getDuration();\n            this.createPluginContainer();\n            this.loaderContainer.hide();\n            this.createChart();\n            this.defineEventListeners();\n        },\n\n        /**\n         * Set events on  load start\n         * @method defineEventListeners\n         */\n        defineEventListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            if (this.settings.withFocus === true) {\n                // resizable\n                this.container.find('.focus-container .' + this.Class.COMPONENT_NAME).resizable({\n                    handles: 'w,e',\n                    containment: \"parent\",\n                    create: function (event) {\n                        $(event.target).find('div.ui-resizable-handle').addClass('ajs-icon ajs-icon-reorder');\n                    },\n                    // start: $.proxy(this.onResizeStart, this),\n                    stop: $.proxy(this.onResizeStop, this)\n                });\n                // draggable\n                this.container.find('.focus-container .' + this.Class.COMPONENT_NAME).draggable({\n                    axis: \"x\",\n                    containment: \"parent\",\n                    stop: $.proxy(this.onDragStop, this)\n                });\n            }\n            if (this.settings.metadataId === '') {\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, $.proxy(this.onSelectedMetadataChange, this));\n            }\n        },\n\n        /**\n         * In charge to create plugin annotation elements\n         * @method createPluginAnnotationElements\n         */\n        createPluginContainer: function () {\n            var componentContainer = $('<div>', {\n                'class': 'components ajs-margin-top'\n            });\n            var component = this.createComponent(this.settings.title);\n            componentContainer.append(component);\n            this.container.append(componentContainer);\n        },\n\n        /**\n         * In charge to create component\n         * @method createComponent\n         */\n        createComponent: function (title) {\n\n            var component = $('<div>', {\n                'class': 'component'\n            });\n\n            var timelineCursor = $('<div>', {\n                'class': \"timeline-cursor\"\n            });\n\n            var msgContainer = $('<p>', {\n                'class': \"title\"\n            });\n            msgContainer.html(title);\n\n            var lineContainerElement = $('<div>', {\n                'class': 'module-chart'\n            });\n            lineContainerElement.append('<svg><svg/>');\n            var lineElement = $('<div>', {\n                'class': 'line'\n            });\n            component.append(timelineCursor);\n            component.append(msgContainer);\n            component.append(lineContainerElement);\n            component.append(lineElement);\n            if (this.settings.withFocus === true) {\n                var focusContainer = $('<div>', {\n                    'class': \"focus-container\"\n                });\n                var focusComponent = $('<div>', {\n                    'class': this.Class.COMPONENT_NAME\n                });\n                focusContainer.append(focusComponent);\n                component.append(focusContainer);\n            }\n\n            return component;\n        },\n\n        /**\n         * In charge to create chart\n         * @method createComponent\n         */\n        createChart: function () {\n            var self = this;\n            nv.addGraph({\n                generate: function () {\n                    // Set chart\n                    self.chart = nv.models.multiBarChart()\n                        .staggerLabels(true)\n                        .stacked(true)\n                        .showYAxis(false)\n                        .color(self.settings.colors)\n                        .controlLabels({\n                            \"grouped\": fr.ina.amalia.player.PlayerMessage.PLUGIN_D3JS_CHART_LABELS[0],\n                            \"stacked\": fr.ina.amalia.player.PlayerMessage.PLUGIN_D3JS_CHART_LABELS[1]\n                        })\n                        .margin({\"left\": 0, \"right\": 0, \"top\": 0, \"bottom\": 50});\n                    self.chart.xAxis.tickFormat(function (d) {\n                        return fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(d, self.settings.framerate, self.settings.timeFormat);\n                    });\n                    self.updateChartData();\n                    nv.utils.windowResize(self.chart.update);\n                    self.chart.multibar.dispatch.on(\"elementClick\", $.proxy(self.onClick, self));\n                    return self.chart;\n                }\n            });\n        },\n\n        /**\n         * In charge to update chart data\n         * @method createComponent\n         */\n        updateChartData: function () {\n            var listOfChartValue = [];\n            for (var idx in this.dataToDeal) {\n                var metadataObject = this.mediaPlayer.getBlockMetadata(this.dataToDeal[idx]);\n                var metadataId = this.dataToDeal[idx].toString();\n                var label = (metadataObject !== null && metadataObject.hasOwnProperty('label')) ? metadataObject.label === \"-\" ? \"\" : metadataObject.label : metadataId;\n                var listOfLocalisations = this.mediaPlayer.getMetadataById(metadataId);\n                this.sortListOfData(listOfLocalisations);\n                listOfChartValue.push(\n                    {\n                        key: label,\n                        values: $.map(listOfLocalisations, function (val) {\n                            return {\n                                x: val.tc,\n                                y: val.score\n                            };\n                        })\n                    }\n                );\n            }\n            d3.select(this.container.find('.module-chart svg').get(0))\n                .datum(listOfChartValue)\n                .transition().duration(500)\n                .call(this.chart);\n        },\n        sortListOfData: function (listOfLocalisations) {\n            // sort by tc\n            listOfLocalisations.sort(function (a, b) {\n                // convert to integers from strings\n                a = parseInt(a.tc);\n                b = parseInt(b.tc);\n                // compare\n                if (a > b) {\n                    return 1;\n                }\n                else if (a < b) {\n                    return -1;\n                }\n                else {\n                    return 0;\n                }\n            });\n        },\n        /**\n         * Return focus start time code\n         * @method getFocusTcin\n         * @returns {Number}\n         */\n        getFocusTcin: function () {\n            var duration = this.tcout - this.tcin;\n            var selectedTcin = parseFloat((duration * this.container.find(\".focus-container .\" + this.Class.COMPONENT_NAME).first().position().left) / this.container.find('.focus-container').first().width());\n            return Math.max(this.tcin, this.tcin + selectedTcin);\n        },\n\n        /**\n         * Return focus end time code\n         * @returns {Number}\n         */\n        getFocusTcout: function () {\n            var focusTcin = this.getFocusTcin();\n            var duration = this.tcout - this.tcin;\n            var selectedTcout = parseFloat((duration * (this.container.find(\".focus-container .\" + this.Class.COMPONENT_NAME).first().width())) / this.container.find('.focus-container').first().width());\n            return Math.min(this.tcout, focusTcin + selectedTcout);\n        },\n\n        /**\n         * Trigger select zone change\n         * @method selectedZoneChange\n         * @returns {Number}\n         */\n        selectedZoneChange: function () {\n            var focusTcin = this.getFocusTcin();\n            var focusTcout = this.getFocusTcout();\n            var mainContainer = this.mediaPlayer.getContainer();\n            // Trigger Focus event\n            mainContainer.trigger(fr.ina.amalia.player.PlayerEventType.ZOOM_RANGE_CHANGE, {\n                focusTcin: focusTcin,\n                focusTcout: focusTcout\n            });\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"selectedZoneChange trigger event :  focusTcin:\" + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(focusTcin, this.settings.framerate, this.settings.timeFormat) + \" focusTcout:\" + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(focusTcout, this.settings.framerate, this.settings.timeFormat));\n            }\n        },\n\n        updateTimelinePos: function (currentTime) {\n            var tc = parseFloat(currentTime);\n            var gtc = this.tcout - this.tcin;\n            this.container.find('.timeline-cursor').css('left', ((tc - this.tcin) * 100) / gtc + '%');\n        },\n\n        /** Player Events * */\n        /**\n         * Fired when plugin ready event\n         * @method onPluginReady\n         * @param {Object} event\n         */\n        onPluginReady: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onPluginReady\");\n            }\n        },\n\n        /**\n         * Fired on begin data change event\n         * @method onBeginDataChange\n         * @param {Object} event\n         */\n        onBeginDataChange: function () {\n            this.loadDataStarted = true;\n            this.dataToDeal = [];\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onBeginDataChange\");\n            }\n        },\n\n        /**\n         * Fired on data change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onDataChange: function (event, data) {\n            if (this.loadDataStarted === true) {\n                var metadataObject = this.mediaPlayer.getBlockMetadata(data.id);\n                if (this.isManagedMetadataType(metadataObject.type) && $.inArray(data.id, this.dataToDeal) < 0) {\n                    this.dataToDeal.push(data.id);\n                }\n            }\n            else {\n                this.updateChartData();\n            }\n        },\n\n        /**\n         * Fired on end data change event\n         * @method onEndDataChange\n         * @param {Object} event\n         */\n        onEndDataChange: function () {\n            this.loadDataStarted = false;\n            this.initializeOnLoadStart();\n        },\n\n        onClick: function (e) {\n            this.mediaPlayer.setCurrentTime(e.data.x);\n        },\n\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         */\n        onSelectedMetadataChange: function (event, data) {\n            this._super(event, data);\n        },\n\n        /**\n         * Fired on cursor resize stop\n         * @param event\n         * @param ui\n         */\n        onResizeStop: function () {\n            this.selectedZoneChange();\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onResizeStop\");\n            }\n        },\n\n        /**\n         * Fired on cursor drag stop\n         * @param event\n         * @param ui\n         */\n        onDragStop: function () {\n            this.selectedZoneChange();\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"onDragStop\");\n            }\n        },\n        /**\n         * Fired on time change event for update time cursor\n         * @method onFirstTimechange\n         * @param {Object} event\n         * @param {Object} data Données renvoyer par l'événement timeupdate.\n         */\n        onTimeupdate: function (event, data) {\n            this.updateTimelinePos(parseFloat(data.currentTime));\n        }\n\n    });"
  },
  {
    "path": "src/plugins/editor/plugin-metadata-block-editor.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage block metadata\n * @class MetadataBlockEditorPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-editor\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.MetadataBlockEditorPlugin\", {\n        classCss: \"ajs-plugin editor plugin-block-editor\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * true if load data started anw\n         * @property loadDataStarted\n         * @default null\n         */\n        loadDataStarted: false,\n        /**\n         * Instance of metadata manager\n         * @property metadataManager\n         * @default null\n         */\n        metadataManager: null,\n        /**\n         * Initialize editor plugin and create container of this plugin\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.settings = $.extend({\n                    shapesListOfElements: [\n                        'legal',\n                        'facetime',\n                        'circle'\n                    ]\n                },\n                this.settings.parameters || {});\n\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.pluginContainer.append(this.container);\n            this.createHeaderElement();\n            this.createMetadataBlock();\n            this.createFooterElement();\n            this.defineListeners();\n            this.metadataManager = this.mediaPlayer.getMetadataManager();\n        },\n        /**\n         * Create block header\n         * @method createHeaderElement\n         */\n        createHeaderElement: function () {\n            var element = $('<div>', {\n                'class': 'heading'\n            });\n            var titleElement = $('<h3>', {\n                'class': 'title',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_LABEL_HEADER\n            });\n            element.append(titleElement);\n            this.container.append(element);\n        },\n        /**\n         * Create metadata list block\n         * @method createHeaderElement\n         */\n        createMetadataBlock: function () {\n            var element = $('<div>', {\n                'class': 'body'\n            });\n            var messagesElement = $('<div>', {\n                'class': 'messages-container'\n            });\n            messagesElement.hide();\n            var formElement = $('<form>', {\n                'class': 'formMetadataBlock'\n            });\n            var idElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_ID\n            });\n            var idElement = $('<input>', {\n                'class': 'metadata-id input',\n                'name': 'id',\n                'type': 'text',\n                'disabled': 'disabled',\n                'placeholder': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_ID\n            });\n            idElementContainer.append(idElement);\n            var lebelElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_LABAL\n            });\n            var labelElement = $('<input>', {\n                'class': 'metadata-label input',\n                'name': 'label',\n                'type': 'text',\n                'placeholder': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_LABAL\n            });\n\n            lebelElementContainer.append(labelElement);\n            var typeElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_TYPE\n            });\n            var typeElement = $('<select>', {\n                'class': 'metadata-type select',\n                'name': 'type'\n            });\n            typeElementContainer.append(typeElement);\n            var typeListOfElements = fr.ina.amalia.player.PluginBindingManager.dataTypes;\n            for (var typeItem in typeListOfElements) {\n                var typeOption = $('<option>', {\n                    'value': typeListOfElements[typeItem].toString(),\n                    'text': typeItem.toString()\n                });\n                typeElement.append(typeOption);\n            }\n            // description\n            var descriptionElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_DESCRIPTION\n            });\n\n            var descriptionElement = $('<textarea>', {\n                'class': 'metadata-description text-area',\n                'name': 'description',\n                'placeholder': \"Description\"\n            });\n            descriptionElementContainer.append(descriptionElement);\n\n            var refElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_REFERENCE\n            });\n\n            var refElement = $('<input>', {\n                'class': 'metadata-ref input',\n                'name': 'ref',\n                'type': 'text',\n                'placeholder': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_REFERENCE\n            });\n            refElementContainer.append(refElement);\n            // author\n            var authorElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_AUTHOR\n            });\n            var authorElement = $('<input>', {\n                'class': 'metadata-author input',\n                'name': 'author',\n                'type': 'text',\n                'placeholder': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_AUTHOR\n            });\n            authorElementContainer.append(authorElement);\n\n            // shapes\n            var shapesElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_SHAPE\n            });\n            var shapesElement = $('<p>', {\n                'class': 'metadata-shape',\n                'placeholder': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_SHAPE\n            });\n            shapesElementContainer.append(shapesElement);\n            var shapesListOfElements = this.settings.shapesListOfElements;\n            for (var shapeItem in shapesListOfElements) {\n                var shapeOption = $('<input>', {\n                    'id': 'shape-item-' + shapesListOfElements[shapeItem].toString(),\n                    'name': 'shape',\n                    'value': shapesListOfElements[shapeItem].toString(),\n                    'type': 'radio'\n                });\n                var item = $('<label>', {\n                    'for': 'shape-item-' + shapesListOfElements[shapeItem].toString(),\n                    'class': 'shape ajs-icon ajs-icon-' + shapesListOfElements[shapeItem].toString()\n                });\n\n                shapesElement.append(shapeOption);\n                shapesElement.append(item);\n            }\n\n            // color\n            var colorElementContainer = $('<div>', {\n                'class': 'form-item',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_COLOR\n            });\n            var colorElement = $('<input>', {\n                'class': 'metadata-color input',\n                'type': 'color',\n                'name': 'color',\n                'placeholder': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_BLOCK_EDITOR_FORM_LABEL_METADATA_COLOR\n            });\n            colorElementContainer.append(colorElement);\n\n            formElement.append(idElementContainer);\n            formElement.append(typeElementContainer);\n            formElement.append(lebelElementContainer);\n            formElement.append(descriptionElementContainer);\n            formElement.append(refElementContainer);\n            formElement.append(authorElementContainer);\n            formElement.append(colorElementContainer);\n            formElement.append(shapesElementContainer);\n            element.append(messagesElement);\n            element.append(formElement);\n            this.container.append(element);\n        },\n        /**\n         * Create block header\n         * @method createHeaderElement\n         */\n        createFooterElement: function () {\n            var element = $('<div>', {\n                'class': 'footer'\n            });\n            var btnAddMetadata = $('<button>', {\n                'class': 'save-metadata ajs-icon ajs-icon-check'\n            }).on('click', {\n                self: this\n            }, this.onSaveMetadata);\n            element.append(btnAddMetadata);\n            this.container.append(element);\n        },\n        /**\n         * In charge to set message error\n         */\n        setMessage: function (messages, messageType) {\n            messageType = (messageType) ? messageType : 'info';\n            if (typeof messages !== 'undefined' && typeof messages.length !== 'undefined') {\n                var container = this.container.find('.messages-container');\n                container.empty().removeClass(function (index, css) {\n                    return (css.match(/(^|\\s)type-\\S+/g) || []).join(' ');\n                }).addClass('type-' + messageType);\n                for (var i = 0;\n                     i < messages.length;\n                     i++) {\n                    var message = $('<p>', {\n                        'class': 'error',\n                        'text': messages[i].toString()\n                    });\n                    container.append(message);\n                }\n\n                if (messages.length !== 0) {\n                    container.show();\n                }\n                else {\n                    container.hide();\n                }\n            }\n        },\n        /**\n         * In charge to clear message container\n         */\n        clearMessage: function () {\n            this.container.find('.messages-container').empty().hide();\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * In charge to validate form\n         */\n        isValidForm: function (errors) {\n            if (typeof errors !== 'undefined' && typeof errors.length !== 'undefined') {\n                var formContainer = this.container.find('.formMetadataBlock');\n                if (formContainer.find('[name=\"id\"]').val() === '') {\n                    errors.push('Id is required field');\n                }\n                else if (formContainer.find('[name=\"label\"]').val() === '') {\n                    errors.push('Label is required field');\n                }\n                else if (formContainer.find('[name=\"type\"]').val() === '') {\n                    errors.push('Type is required field');\n                }\n                return (errors.length === 0);\n            }\n            return false;\n        },\n        updateMetadata: function (id) {\n            var metadataBlock = this.metadataManager.getBlockMetadata(id);\n            var viewControl = (metadataBlock.hasOwnProperty('viewControl') && metadataBlock.viewControl !== null) ? metadataBlock.viewControl : null;\n            var shape = (viewControl !== null && viewControl.hasOwnProperty('shape') && viewControl.shape !== \"\") ? viewControl.shape : 'circle';\n            var color = (viewControl !== null && viewControl.hasOwnProperty('color') && viewControl.color !== \"\") ? viewControl.color : '#3cf';\n            var formContainer = this.container.find('.formMetadataBlock');\n            if (metadataBlock !== null) {\n                formContainer.find('[name=\"label\"]').val(metadataBlock.hasOwnProperty('label') ? metadataBlock.label : '');\n                formContainer.find('[name=\"type\"] option[value=\"' + metadataBlock.type + '\"]').prop('selected', true);\n                if (metadataBlock.type === \"\" || metadataBlock.type === fr.ina.amalia.player.PluginBindingManager.dataTypes.DEFAULT) {\n                    formContainer.find('[name=\"type\"]').prop('disabled', false);\n                }\n                else {\n                    formContainer.find('[name=\"type\"]').prop('disabled', true);\n                }\n                formContainer.find('[name=\"description\"]').val(metadataBlock.hasOwnProperty('description') ? metadataBlock.description : '');\n                formContainer.find('[name=\"ref\"]').val(metadataBlock.hasOwnProperty('ref') ? metadataBlock.ref : '');\n                formContainer.find('[name=\"author\"]').val(metadataBlock.hasOwnProperty('author') ? metadataBlock.author : '');\n                formContainer.find('[name=\"color\"]').val(color);\n                formContainer.find('[name=\"shape\"]').first().prop('checked', true);\n                formContainer.find('[name=\"shape\"][value=\"' + shape + '\"]').prop('checked', true);\n            }\n            else {\n                formContainer.find('[name=\"label\"]').val('');\n                formContainer.find('[name=\"type\"] option').prop('selected', false);\n                formContainer.find('[name=\"type\"]').prop('disabled', false);\n                formContainer.find('[name=\"description\"]').val('');\n                formContainer.find('[name=\"ref\"]').val('');\n                formContainer.find('[name=\"author\"]').val('');\n                formContainer.find('[name=\"color\"]').val('');\n                formContainer.find('[name=\"shape\"]').first().prop('checked', true);\n            }\n            this.container.find('form.formMetadataBlock .form-item .metadata-id').val(id);\n            this.clearMessage();\n        },\n        /**\n         * In charge to save form\n         */\n        saveMetadata: function () {\n            var formContainer = this.container.find('.formMetadataBlock');\n            var errors = [];\n            if (this.isValidForm(errors)) {\n                var metadataId = formContainer.find('[name=\"id\"]').val();\n                this.metadataManager.updateBlockMetadata(metadataId, {\n                    id: metadataId,\n                    label: fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formContainer.find('[name=\"label\"]').val()),\n                    type: formContainer.find('[name=\"type\"]').val(),\n                    description: fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formContainer.find('[name=\"description\"]').val()),\n                    ref: fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formContainer.find('[name=\"ref\"]').val()),\n                    author: fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formContainer.find('[name=\"author\"]').val()),\n                    viewControl: {\n                        color: fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formContainer.find('[name=\"color\"]').val()),\n                        shape: fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formContainer.find('[name=\"shape\"]:checked').val())\n                    }\n                });\n                this.updateMetadata(metadataId);\n                this.setMessage([\n                    'Saved!'\n                ], 'info');\n            }\n            else {\n                this.setMessage(errors, 'error');\n            }\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         * @param {Object} e\n         * @param {Object} data\n         */\n        onSelectedMetadataChange: function (e, data) {\n            if (data.metadataId !== null) {\n                e.data.self.updateMetadata(data.metadataId);\n            }\n        },\n        /**\n         * Called on click to save btn\n         * @param {Object} e\n         */\n        onSaveMetadata: function (e) {\n            e.data.self.saveMetadata();\n        }\n    });\n"
  },
  {
    "path": "src/plugins/editor/plugin-metadata-item-editor.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage selected items\n * @class MetadataItemsEditorPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-editor\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.MetadataItemEditorPlugin\", {\n        classCss: \"ajs-plugin plugin-selected-item-editor\",\n        addSegmentIcon: \"ajs-icon ajs-icon-add-whole-segment\",\n        msgAddSegment: 'AJOUTER UN SEGMENT INTEGRALE',\n        msgValid: 'Créer un élément',\n        msgEdit: 'Modifier l\\'élément'\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        metadataId: null,\n        selectedItem: null,\n        /**\n         * Initialize\n         * @method initialize\n         */\n        initialize: function () {\n            this.metadataId = null;\n            this.selectedItem = null;\n            this._super();\n            this.settings = $.extend({\n                timeFormat: 'f',\n                framerate: this.settings.framerate,\n                defaultPercentWidth: 0.1\n            }, this.settings.parameters || {});\n\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.pluginContainer.append(this.container);\n            this.createAutoFillUpForm();\n            this.createFormContainer();\n            this.defineListeners();\n        },\n        createFormItem: function (id, label, type) {\n            var formItem = $('<div>', {\n                'class': 'form-item ' + id\n            });\n\n            var labelFormItem = $('<input>', {\n                'class': 'form-item-' + id,\n                'type': type,\n                'placeholder': label\n            });\n            formItem.append(labelFormItem);\n            return formItem;\n        },\n        createControl: function (action, label) {\n            var formItem = $('<div>', {\n                'class': 'form-item ctrl ' + action\n            });\n\n            var controlItem = $('<button>', {\n                'class': 'form-ctrl-item-' + action,\n                'text': label\n            });\n\n            formItem.append(controlItem);\n\n            return formItem;\n        },\n        createAutoFillUpForm: function () {\n            var formContainer = $('<div>', {\n                'class': 'auto-fill-up-form'\n            });\n            var message = $('<div>', {\n                'class': 'message',\n                'text': this.Class.msgAddSegment\n            });\n            formContainer.append(message);\n            formContainer.append(this.createControl('yes', 'Oui'));\n            formContainer.append(this.createControl('no', 'NON'));\n            this.container.append(formContainer);\n        },\n        /**\n         * Create metadata list block\n         * @method createFormContainer\n         */\n        createFormContainer: function () {\n            var formContainer = $('<div>', {\n                'class': 'form'\n            });\n            //Label\n            formContainer.append(this.createFormItem('label', 'Titre', 'text'));\n            //Tc in\n            formContainer.append(this.createControl('tcin', 'TC-IN'));\n            formContainer.append(this.createFormItem('tcin', '00:00:00:00', 'text'));\n            //TC Out\n            formContainer.append(this.createControl('tcout', 'TC-OUT'));\n            formContainer.append(this.createFormItem('tcout', '00:00:00:00', 'text'));\n            //Valid\n            formContainer.append(this.createControl('valid', this.Class.msgValid));\n            //Delete\n            formContainer.append(this.createControl('delete', 'Supprimer'));\n            formContainer.append(this.createControl('close-item', 'Annuler'));\n            //Create full segment\n            var c = this.createControl('auto-fill-up', '');\n            c.find('button').addClass(this.Class.addSegmentIcon);\n            formContainer.append(c);\n\n            this.container.append(formContainer);\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            // Auto complete form\n            this.container.find('.form .form-item .form-ctrl-item-auto-fill-up').on('click', {\n                self: this\n            }, this.onClickOpenAutoCompletForm);\n            this.container.find('.auto-fill-up-form .form-ctrl-item-no').on('click', {\n                self: this\n            }, this.onClickCloseAutoCompletForm);\n            this.container.find('.auto-fill-up-form .form-ctrl-item-yes').on('click', {\n                self: this\n            }, this.onClickAutoCompletForm);\n\n\n            //Add Form\n            this.container.find('.form .form-ctrl-item-tcin').on('click', {\n                self: this\n            }, this.onClickToTcin);\n            this.container.find('.form .form-ctrl-item-tcout').on('click', {\n                self: this\n            }, this.onClickToTcout);\n            this.container.find('.form .form-item-tcin').on('keyup', {\n                self: this\n            }, this.onFormValuesChange);\n            this.container.find('.form .form-item-tcin').on('change', {\n                self: this\n            }, this.onFormValuesChange);\n            this.container.find('.form .form-item-tcout').on('keyup', {\n                self: this\n            }, this.onFormValuesChange);\n            this.container.find('.form .form-item-tcout').on('change', {\n                self: this\n            }, this.onFormValuesChange);\n\n            this.container.find('.form .form-item .form-ctrl-item-valid').on('click', {\n                self: this\n            }, this.onFormValid);\n            this.container.find('.form .form-item .form-ctrl-item-delete').on('click', {\n                self: this\n            }, this.onDeleteItem);\n            this.container.find('.form .form-item .form-ctrl-item-close-item').on('click', {\n                self: this\n            }, this.onCloseItem);\n\n            var mainContainer = this.mediaPlayer.getContainer();\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                self: this\n            }, this.onDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_ITEMS_CHANGE, {\n                self: this\n            }, this.onSelectedItemsChange);\n            //On select metadata\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        addItem: function () {\n            if (this.metadataId !== null) {\n                var label = this.container.find('.form input.form-item-label').val();\n                var tcin = this.container.find('.form input.form-item-tcin').val();\n                var tcout = this.container.find('.form input.form-item-tcout').val();\n                //Create new data block\n                if (this.selectedItem === null) {\n                    var dataList = [\n                        {\n                            'tcin': fr.ina.amalia.player.helpers.UtilitiesHelper.convertTimeFPSToSeconde(tcin),\n                            'tcout': fr.ina.amalia.player.helpers.UtilitiesHelper.convertTimeFPSToSeconde(tcout),\n                            'label': (label === \"\") ? this.getLabel() : label\n                        }\n                    ];\n                    this.mediaPlayer.addMetadataById(this.metadataId, dataList);\n                    this.clearSelectedData();\n                }\n                else if (this.selectedItem !== null && this.selectedItem.hasOwnProperty('label') && this.selectedItem.hasOwnProperty('tcin') && this.selectedItem.hasOwnProperty('tcout')) {\n                    this.selectedItem.label = (label === \"\") ? this.getLabel() : label;\n                    this.selectedItem.tcin = fr.ina.amalia.player.helpers.UtilitiesHelper.convertTimeFPSToSeconde(tcin);\n                    this.selectedItem.tcout = fr.ina.amalia.player.helpers.UtilitiesHelper.convertTimeFPSToSeconde(tcout);\n                    this.clearSelectedData();\n                    this.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                        id: this.metadataId\n                    });\n                }\n                //Clear edit mode\n                this.container.find('.form').removeClass('edit-mode');\n            }\n        },\n        getLabel: function () {\n            var label = \"\";\n            var blockMetadata = this.mediaPlayer.getBlockMetadata(this.metadataId);\n            var countLoc = $(this.mediaPlayer.getMetadataById(this.metadataId)).size();\n            if (blockMetadata !== null && blockMetadata.hasOwnProperty('label')) {\n                label = blockMetadata.label + ' - ' + countLoc;\n            }\n            return label;\n        },\n        /**\n         * Add auto item with tcin and tcout\n         */\n        addItemAuto: function () {\n            if (this.metadataId !== null) {\n                var dataList = [\n                    {\n                        'tcin': this.mediaPlayer.getTcin(),\n                        'tcout': this.mediaPlayer.getTcout(),\n                        'label': this.getLabel()\n                    }\n                ];\n                this.mediaPlayer.addMetadataById(this.metadataId, dataList);\n                this.clearSelectedData();\n                return true;\n            }\n            return false;\n        },\n        clearSelectedData: function () {\n            if (this.selectedItem !== null && this.selectedItem.hasOwnProperty('selected')) {\n                this.selectedItem.selected = false;\n            }\n            this.selectedItem = null;\n            //clear data\n            this.container.find('.form').removeClass('edit-mode');\n            this.container.find('.form input.form-item-label').val(\"\");\n            this.container.find('.form input.form-item-tcin').val(\"\");\n            this.container.find('.form input.form-item-tcout').val(\"\");\n            this.container.find('.form .form-item .form-ctrl-item-delete').removeClass(\"on\");\n            this.container.find('.form .form-item .form-ctrl-item-close-item').removeClass(\"on\");\n            this.container.find('.form .form-item .form-ctrl-item-valid').text(this.Class.msgValid);\n            this.container.find('.form .form-item .form-ctrl-item-valid').removeClass(\"valid\");\n            this.container.find('.form').removeClass(\"valid\");\n            this.container.find('.form input.form-item-tcout').removeClass(\"error\").removeClass(\"valid\");\n            this.container.find('.form input.form-item-tcin').removeClass(\"error\").removeClass(\"valid\");\n        },\n        validFormItems: function () {\n            var isValid = true;\n            var timePattern = /^[0-9]{2}:[0-9]{2}:[0-9]{2}:[0-9]{2}$/;\n            var tcin = this.container.find('.form input.form-item-tcin').val();\n            var tcout = this.container.find('.form input.form-item-tcout').val();\n            if (timePattern.test(tcin) !== true) {\n                this.container.find('.form input.form-item-tcin').addClass(\"error\").removeClass(\"valid\");\n                isValid = false;\n            }\n            else {\n                this.container.find('.form input.form-item-tcin').removeClass(\"error\").addClass(\"valid\");\n            }\n            if (timePattern.test(tcout) !== true) {\n                this.container.find('.form input.form-item-tcout').addClass(\"error\").removeClass(\"valid\");\n                isValid = false;\n            }\n            else {\n                this.container.find('.form input.form-item-tcout').removeClass(\"error\").addClass(\"valid\");\n            }\n\n            if (fr.ina.amalia.player.helpers.UtilitiesHelper.convertTimeFPSToSeconde(tcin) >= fr.ina.amalia.player.helpers.UtilitiesHelper.convertTimeFPSToSeconde(tcout)) {\n                this.container.find('.form input.form-item-tcin').addClass(\"error\");\n                this.container.find('.form input.form-item-tcout').addClass(\"error\");\n                isValid = false;\n            }\n            if (this.selectedItem === null) {\n                this.container.find('.form .form-item .form-ctrl-item-delete').removeClass(\"on\");\n                this.container.find('.form .form-item .form-ctrl-item-close-item').removeClass(\"on\");\n                this.container.find('.form .form-item .form-ctrl-item-valid').text(this.Class.msgValid);\n            }\n            else {\n                this.container.find('.form .form-item .form-ctrl-item-delete').addClass(\"on\");\n                this.container.find('.form .form-item .form-ctrl-item-close-item').addClass(\"on\");\n                this.container.find('.form .form-item .form-ctrl-item-valid').text(this.Class.msgEdit);\n            }\n            if (isValid) {\n                this.container.find('.form .form-item .form-ctrl-item-valid').addClass(\"valid\");\n                this.container.find('.form').addClass(\"valid\");\n            }\n            else {\n                this.container.find('.form .form-item .form-ctrl-item-valid').removeClass(\"valid\");\n                this.container.find('.form').removeClass(\"valid\");\n            }\n\n            return isValid;\n        },\n\n        setTcin: function () {\n            this.container.find('.form input.form-item-tcin').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(this.mediaPlayer.getCurrentTime(), this.settings.framerate, this.settings.timeFormat));\n            if (this.container.find('.form input.form-item-tcout').val() === \"\") {\n                var tcout = Math.min(this.mediaPlayer.getCurrentTime() + (this.mediaPlayer.getDuration() * Math.min(this.settings.defaultPercentWidth, 1)), this.mediaPlayer.getDuration());\n                this.container.find('.form input.form-item-tcout').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tcout, this.settings.framerate, this.settings.timeFormat));\n                this.validFormItems();\n                this.container.find('.form input.form-item-tcout').removeClass(\"error\").removeClass(\"valid\");\n            }\n            else {\n                this.validFormItems();\n            }\n\n        },\n        setTcout: function () {\n            this.container.find('.form input.form-item-tcout').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(this.mediaPlayer.getCurrentTime(), this.settings.framerate, this.settings.timeFormat));\n            this.validFormItems();\n        },\n        /**\n         * in charge to valid form\n         */\n        validForm: function () {\n            if (this.validFormItems()) {\n                this.addItem();\n            }\n        },\n\n        onFormValuesChange: function (event) {\n            event.data.self.validFormItems();\n        },\n        onClickToTcin: function (event) {\n            event.data.self.setTcin();\n        },\n        onClickToTcout: function (event) {\n            event.data.self.setTcout();\n        },\n        /**\n         * Fired on selected item\n         * @method onSelectedItemsChange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectedItemsChange: function (event, data) {\n            event.data.self.selectedItem = (typeof data !== \"undefined\" && data.hasOwnProperty(\"item\")) ? data.item : null;\n\n            if (event.data.self.selectedItem !== null && event.data.self.selectedItem.selected === false) {\n                event.data.self.selectedItem = null;\n                event.data.self.clearSelectedData();\n            }\n            else if (event.data.self.selectedItem !== null && event.data.self.selectedItem.hasOwnProperty('label') && event.data.self.selectedItem.hasOwnProperty('tcin') && event.data.self.selectedItem.hasOwnProperty('tcout')) {\n                event.data.self.container.find('.form').addClass('edit-mode');\n                event.data.self.container.find('.form input.form-item-label').val(event.data.self.selectedItem.label);\n                event.data.self.container.find('.form input.form-item-tcin').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(event.data.self.selectedItem.tcin, event.data.self.settings.framerate, event.data.self.settings.timeFormat));\n                event.data.self.container.find('.form input.form-item-tcout').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(event.data.self.selectedItem.tcout, event.data.self.settings.framerate, event.data.self.settings.timeFormat));\n                event.data.self.validFormItems();\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectedItemsChange\");\n            }\n        },\n        /**\n         * Fired on data change event\n         * @method onDataChange\n         * @param {Object} event\n         */\n        onDataChange: function (event) {\n            if (event.data.self.selectedItem !== null && event.data.self.selectedItem.hasOwnProperty('label') && event.data.self.selectedItem.hasOwnProperty('tcin') && event.data.self.selectedItem.hasOwnProperty('tcout')) {\n                event.data.self.container.find('.form input.form-item-label').val(event.data.self.selectedItem.label);\n                event.data.self.container.find('.form input.form-item-tcin').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(event.data.self.selectedItem.tcin, event.data.self.settings.framerate, 'f'));\n                event.data.self.container.find('.form input.form-item-tcout').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(event.data.self.selectedItem.tcout, event.data.self.settings.framerate, 'f'));\n                event.data.self.validFormItems();\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectedItemsChange\");\n            }\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectedMetadataChange: function (event, data) {\n            event.data.self.mediaPlayer.removeAllSelectedItems();\n            event.data.self.metadataId = data.metadataId !== null ? data.metadataId.toString() : null;\n\n        },\n        onClickOpenAutoCompletForm: function (event) {\n            event.data.self.container.find('.auto-fill-up-form').addClass('on');\n        },\n        onClickCloseAutoCompletForm: function (event) {\n            event.data.self.container.find('.auto-fill-up-form').removeClass('on');\n        },\n        onClickAutoCompletForm: function (event) {\n            event.data.self.addItemAuto();\n            event.data.self.container.find('.auto-fill-up-form').removeClass('on');\n        },\n        onFormValid: function (event) {\n            event.data.self.validForm();\n        },\n        /**\n         * In charge to delete item\n         * @method onDeleteItem\n         * @param {Object} event\n         */\n        onDeleteItem: function (event) {\n            event.data.self.selectedItem.tc = null;\n            event.data.self.selectedItem.tcin = null;\n            event.data.self.selectedItem.tcout = null;\n            event.data.self.selectedItem.label = null;\n            event.data.self.selectedItem.selected = false;\n            event.data.self.selectedItem.formCreated = false;\n            event.data.self.selectedItem.deleted = true;\n            event.data.self.selectedItem = null;\n            event.data.self.clearSelectedData();\n\n            //Send data change evnet\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: event.data.self.metadataId\n            });\n        },\n        onCloseItem: function (event) {\n            event.data.self.selectedItem = null;\n            event.data.self.mediaPlayer.removeAllSelectedItems();\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: event.data.self.metadataId\n            });\n            event.data.self.clearSelectedData();\n        }\n    });\n\n"
  },
  {
    "path": "src/plugins/editor/plugin-metadata-items-editor.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage selected items\n * @class MetadataItemsEditorPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-editor\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.MetadataItemsEditorPlugin\", {\n        classCss: \"ajs-plugin editor plugin-items-editor\"\n    },\n    {\n        /**\n         * Start time code\n         * @property tcin\n         * @default 0\n         */\n        tcin: 0,\n        /**\n         * End time code\n         * @property tcout\n         * @default 0\n         */\n        tcout: 0,\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * true if load data started anw\n         * @property loadDataStarted\n         * @default null\n         */\n        loadDataStarted: false,\n        /**\n         * Instance of metadata manager\n         * @property metadataManager\n         * @default null\n         */\n        metadataManager: null,\n        /**\n         * Selected metadata id\n         * @property metadataId\n         * @default null\n         */\n        metadataId: null,\n        /**\n         * Localisation manager\n         * @property localisationManager\n         * @type {Object}\n         * @default null\n         */\n        localisationManager: null,\n        /**\n         * Initialize\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.settings = $.extend({\n                defaultPercentWidth: 0.1\n            }, this.settings.parameters || {});\n\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.tcin = this.mediaPlayer.getTcOffset();\n            this.tcout = this.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset();\n            this.pluginContainer.append(this.container);\n            this.createHeaderElement();\n            this.createMetadataItemsBlock();\n            this.createFooterElement();\n            this.defineListeners();\n            this.metadataManager = this.mediaPlayer.getMetadataManager();\n            this.localisationManager = new fr.ina.amalia.player.LocalisationManager();\n        },\n        /**\n         * Create block header\n         * @method createHeaderElement\n         */\n        createHeaderElement: function () {\n            var element = $('<div>', {\n                'class': 'heading'\n            });\n            var titleElement = $('<h3>', {\n                'class': 'title',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_LABEL_HEADER\n            });\n            element.append(titleElement);\n            this.container.append(element);\n        },\n        /**\n         * Create metadata list block\n         * @method createHeaderElement\n         */\n        createMetadataItemsBlock: function () {\n            var element = $('<div>', {\n                'class': 'body'\n            });\n            var messagesElement = $('<div>', {\n                'class': 'messages-container'\n            }).hide();\n            var listOfSelectedItemsElement = $('<ul>', {\n                'class': 'listOfSelectedItems'\n            });\n\n            element.append(messagesElement);\n            element.append(listOfSelectedItemsElement);\n\n            this.container.append(element);\n        },\n        /**\n         * Create block header\n         * @method createHeaderElement\n         */\n        createFooterElement: function () {\n            var element = $('<div>', {\n                'class': 'footer'\n            });\n            var addBtnElement = $('<span>', {\n                'class': 'button add ajs-icon ajs-icon-plus'\n            }).on('click', {\n                self: this\n            }, this.onAddItem);\n            var validateBtnElement = $('<span>', {\n                'class': 'button validateAll ajs-icon ajs-icon-check'\n            }).on('click', {\n                self: this\n            }, this.onValidateItems);\n\n            var clearBtnElement = $('<span>', {\n                'class': 'button clear ajs-icon ajs-icon-eject'\n            }).on('click', {\n                self: this\n            }, this.onClearItems);\n\n            element.append(addBtnElement);\n            element.append(validateBtnElement);\n            element.append(clearBtnElement);\n            this.container.append(element);\n        },\n        /**\n         * In charge to set message error\n         * @param {type} messages\n         * @param {type} messageType\n         * @returns {undefined}\n         */\n        setMessage: function (messages, messageType) {\n            messageType = (messageType) ? messageType : 'info';\n            if (typeof messages !== 'undefined' && typeof messages.length !== 'undefined') {\n                var container = this.container.find('.messages-container');\n                container.empty().removeClass(function (index, css) {\n                    return (css.match(/(^|\\s)type-\\S+/g) || []).join(' ');\n                }).addClass('type-' + messageType);\n                for (var i = 0;\n                     i < messages.length;\n                     i++) {\n                    var message = $('<p>', {\n                        'class': 'error',\n                        'text': messages[i].toString()\n                    });\n                    container.append(message);\n                }\n\n                if (messages.length !== 0) {\n                    container.show();\n                }\n                else {\n                    container.hide();\n                }\n            }\n        },\n        /**\n         * In charge to clear message container\n         */\n        clearMessage: function () {\n            var container = this.container.find('.messages-container');\n            container.empty().hide();\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_ITEMS_CHANGE, {\n                self: this\n            }, this.onSelectedItemsChange);\n            //On data change event\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                self: this\n            }, this.onDataChange);\n\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * In charge to create form by item\n         * @param {type} data\n         * @returns {Object}\n         */\n        createFormData: function (data) {\n            var formData = $('<form>', {\n                'class': 'form-data'\n            }).data('metadata', data);\n            // Label\n            var labelElementContainer = $('<div>', {\n                'class': 'form-input'\n            });\n\n            var labelTextElement = $('<span>', {\n                'class': 'text data-model-label',\n                'data-label': '',\n                'text': data.hasOwnProperty('label') ? data.label : ''\n            });\n            var labelElement = $('<input>', {\n                'name': 'label',\n\n                'class': 'input data-model-label',\n                'value': data.hasOwnProperty('label') ? data.label : '',\n                'placeholder': 'Label'\n            }).hide();\n\n            labelElementContainer.append(labelTextElement);\n            labelElementContainer.append(labelElement);\n            formData.append(labelElementContainer);\n\n            if (data.hasOwnProperty('tcout')) {\n                // Tc In\n                var tcInElementContainer = $('<div>', {\n                    'class': 'form-input'\n                });\n                var tcInTextElement = $('<span>', {\n                    'class': 'text  data-model-tcin',\n                    'data-label': 'Tc-in: ',\n                    'text': data.hasOwnProperty('tcin') ? 'Tc-in: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcin, 'ms') : ''\n                });\n                var tcInElement = $('<input>', {\n                    'name': 'tcin',\n                    'class': 'input  data-model-tcin',\n                    'value': data.hasOwnProperty('tcin') ? fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcin, 'ms') : '',\n                    'placeholder': 'TC-IN'\n                }).hide();\n                tcInElementContainer.append(tcInTextElement);\n                tcInElementContainer.append(tcInElement);\n                formData.append(tcInElementContainer);\n\n                // Tc Out\n                var tcOutElementContainer = $('<div>', {\n                    'class': 'form-input'\n                });\n                var tcOutTextElement = $('<span>', {\n                    'class': 'text data-model-tcout',\n                    'data-label': 'Tc-out: ',\n                    'text': data.hasOwnProperty('tcout') ? 'Tc-out: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcout, 'ms') : ''\n                });\n                var tcOutElement = $('<input>', {\n                    'name': 'tcout',\n                    'class': 'input data-model-tcout',\n                    'value': data.hasOwnProperty('tcout') ? fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcout, 'ms') : '',\n                    'placeholder': 'TC-OUT'\n                }).hide();\n                tcOutElementContainer.append(tcOutTextElement);\n                tcOutElementContainer.append(tcOutElement);\n                formData.append(tcOutElementContainer);\n            }\n            else {\n                var tc = (typeof data.tc === \"number\") ? data.tc : fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(data.tc);\n                // Tc\n                var tcElementContainer = $('<div>', {\n                    'class': 'form-input'\n                });\n                var tcTextElement = $('<span>', {\n                    'class': 'text data-model-tc',\n                    'data-label': 'Tc: ',\n                    'text': data.hasOwnProperty('tc') ? 'Tc: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc, 'ms') : ''\n                });\n                var tcElement = $('<input>', {\n                    'name': 'tc',\n                    'class': 'input data-model-tc',\n                    'value': data.hasOwnProperty('tc') ? fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc, 'ms') : '',\n                    'placeholder': 'TC'\n                }).hide();\n                tcElementContainer.append(tcTextElement);\n                tcElementContainer.append(tcElement);\n                formData.append(tcElementContainer);\n            }\n\n            // Submit\n            var controlsElementContainer = $('<div>', {\n                'class': 'form-controls'\n            });\n            var submitBtnElement = $('<span>', {\n                // 'text' : 'valid',\n                'class': 'button valid ajs-icon ajs-icon-check'\n            });\n\n            var removeBtnElement = $('<span>', {\n                // 'text' : 'valid',\n                'class': 'button remove ajs-icon ajs-icon-eraser'\n            });\n\n            var closeBtnElement = $('<span>', {\n                // 'text' : 'valid',\n                'class': 'button close ajs-icon ajs-icon-eject'\n            });\n\n            controlsElementContainer.append(submitBtnElement);\n            controlsElementContainer.append(removeBtnElement);\n            controlsElementContainer.append(closeBtnElement);\n\n            formData.append(controlsElementContainer);\n\n            return formData;\n        },\n        /**\n         * In charge to update selected items\n         */\n        updateSelectedItems: function () {\n            var listOfSelectedItemsElement = this.container.find('ul.listOfSelectedItems');\n            var listOfSelectedItems = this.mediaPlayer.getSelectedItems();\n            var lastItem = listOfSelectedItemsElement.find('li.item').last();\n            var oldIdx = lastItem.length > 0 ? parseInt(lastItem.attr('class').match(/item-([0-9]+)/)[1]) + 1 : 0;\n            oldIdx = isNaN(oldIdx) ? 0 : oldIdx;\n            // Clear old data\n            // listOfSelectedItemsElement.empty();\n            if (typeof listOfSelectedItems !== 'undefined' && listOfSelectedItems.hasOwnProperty('length') && listOfSelectedItems.length > 0) {\n                for (var i = 0;\n                     i < listOfSelectedItems.length;\n                     i++) {\n                    var data = listOfSelectedItems[i];\n                    var addForm = ( data !== null && data.hasOwnProperty('formCreated')) ? data.formCreated === false : true;\n                    if (data !== null && addForm) {\n\n                        var itemForm = this.createFormData(data);\n                        var item = $('<li>', {\n                            'class': 'item item-' + i\n                        });\n                        data.formCreated = true;\n                        item.append(itemForm);\n                        listOfSelectedItemsElement.append(item);\n                    }\n                }\n            }\n            // Add events\n            listOfSelectedItemsElement.find('.form-input').on('click', {\n                self: this\n            }, this.onFormEdit);\n            listOfSelectedItemsElement.find('.form-input input.input').on('focusout', {\n                self: this\n            }, this.onFormFocusout);\n            listOfSelectedItemsElement.find('.form-data .form-controls .button.valid').on('click', {\n                self: this\n            }, this.onSaveForm);\n            listOfSelectedItemsElement.find('.form-data .form-controls .button.remove').on('click', {\n                self: this\n            }, this.onDeleteItem);\n            listOfSelectedItemsElement.find('.form-data .form-controls .button.close').on('click', {\n                self: this\n            }, this.onCloseForm);\n        },\n        /**\n         * In charge close item\n         * @param {Object} itemDataElement\n         */\n        _closeItem: function (itemDataElement) {\n            // var itemIdx = parseInt(itemDataElement.attr('class').match(/item-([0-9]+)/)[1]);\n            var formData = itemDataElement.find('form').first();\n            var itemData = formData.data('metadata');\n            itemData.selected = false;\n            itemData.formCreated = false;\n            itemDataElement.remove();\n            //this.metadataManager.removeSelectedItem(itemIdx);\n        },\n        /**\n         * In charge of validate item\n         * @param {Object} itemDataElement\n         */\n        _validateItem: function (itemDataElement) {\n            var itemIdx = parseInt(itemDataElement.attr('class').match(/item-([0-9]+)/)[1]);\n            var formData = itemDataElement.find('form').first();\n            var itemData = formData.data('metadata');\n            var timePattern = /^[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{2}$/;\n            var isValid = false;\n\n            if (timePattern.test(formData.find('[name=\"tc\"]').val())) {\n                isValid = true;\n            }\n            else if (typeof formData.find('[name=\"tcin\"]').val() !== \"undefined\" && typeof formData.find('[name=\"tcout\"]').val() !== \"undefined\") {\n                if (timePattern.test(formData.find('[name=\"tcin\"]').val()) && timePattern.test(formData.find('[name=\"tcout\"]').val())) {\n                    isValid = true;\n                }\n                else {\n                    isValid = false;\n                }\n            }\n\n            // Set values\n            itemData.tc = fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(formData.find('[name=\"tc\"]').val());\n            if (this.validItemTcRange(itemData, itemData.tc)) {\n                isValid = true;\n            }\n            else if (itemData.hasOwnProperty(\"tcRange\")) {\n                isValid = false;\n            }\n\n            if (isValid) {\n                if (typeof formData.find('[name=\"tcout\"]').val() !== \"undefined\") {\n                    itemData.tcin = fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(formData.find('[name=\"tcin\"]').val());\n                    itemData.tcout = fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(formData.find('[name=\"tcout\"]').val());\n                }\n                itemData.label = fr.ina.amalia.player.helpers.UtilitiesHelper.htmlEscape(formData.find('[name=\"label\"]').val());\n                itemData.selected = false;\n                itemData.formCreated = false;\n                itemDataElement.remove();\n                this.metadataManager.removeSelectedItem(itemIdx);\n            }\n            return isValid;\n        },\n        /**\n         * In charge to valid item tc range\n         * @method validItemTcRange\n         * @param {Object} item\n         * @param {Object} tc\n         */\n        validItemTcRange: function (item, tc) {\n            var isValid = false;\n            if (item !== null && typeof item === \"object\" && item.hasOwnProperty(\"tcRange\")) {\n                if (item.tcRange.hasOwnProperty('min') && item.tcRange.hasOwnProperty('max')) {\n                    isValid = tc > item.tcRange.min && tc < item.tcRange.max;\n                }\n                else if (item.tcRange.hasOwnProperty('min')) {\n                    isValid = tc > item.tcRange.min && tc <= this.tcout;\n                }\n                else if (item.tcRange.hasOwnProperty('max')) {\n                    isValid = tc >= this.tcin && tc < item.tcRange.max;\n                }\n            }\n            return isValid;\n        },\n        /**\n         * In charge to update selected form items when data change event\n         * @param {Object} targetElement\n         */\n        updateFormItems: function () {\n            var listOfSelectedItemsElement = this.container.find('ul.listOfSelectedItems li.item');\n            for (var selectedItemElementIdx = 0;\n                 selectedItemElementIdx < listOfSelectedItemsElement.length;\n                 selectedItemElementIdx++) {\n                var dataItem = $(listOfSelectedItemsElement[selectedItemElementIdx]).find('form.form-data').data('metadata');\n\n                if (typeof dataItem === \"object\" && dataItem.hasOwnProperty('selected') && dataItem.selected === true) {\n                    $.each($(listOfSelectedItemsElement[selectedItemElementIdx]).find('.form-data'), this.updateFormItem);\n                }\n                else if (typeof dataItem === \"object\" && dataItem.hasOwnProperty('selected') && dataItem.selected === false) {\n                    this._closeItem($(listOfSelectedItemsElement[selectedItemElementIdx]));\n                }\n            }\n        },\n        updateFormItem: function (idx, element) {\n            var targetElement = $(element);\n            var data = targetElement.data('metadata');\n            if (data.hasOwnProperty('label')) {\n                targetElement.find('span.data-model-label').html(data.label);\n                targetElement.find('input.data-model-label').val(data.label);\n            }\n            if (data.hasOwnProperty('tc')) {\n                targetElement.find('span.data-model-tc').html(targetElement.find('span.data-model-tc').attr('data-label') + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tc, 'ms'));\n                targetElement.find('input.data-model-tc').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tc, 'ms'));\n            }\n            if (data.hasOwnProperty('tcin')) {\n                targetElement.find('span.data-model-tcin').html(targetElement.find('span.data-model-tcin').attr('data-label') + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcin, 'ms'));\n                targetElement.find('input.data-model-tcin').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcin, 'ms'));\n            }\n            if (data.hasOwnProperty('tcout')) {\n                targetElement.find('span.data-model-tcout').html(targetElement.find('span.data-model-tcout').attr('data-label') + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcout, 'ms'));\n                targetElement.find('input.data-model-tcout').val(fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcout, 'ms'));\n            }\n        },\n        /**\n         * In charge to open selected input\n         * @param {Object} event\n         */\n        onFormEdit: function (event) {\n            var currentElement = $(event.currentTarget);\n            currentElement.find('.text').html('').hide();\n            currentElement.find('input').show().focus();\n        },\n        /**\n         * Fired on focus out of item\n         * @param {Object} event\n         */\n        onFormFocusout: function (event) {\n            var currentElement = $(event.target);\n            var textElement = currentElement.parent().find('.text');\n            textElement.text(textElement.attr('data-label') + currentElement.val()).show();\n            currentElement.parents('li.item').find('.button.valid').addClass('on');\n            currentElement.hide();\n        },\n        /**\n         * In charge to save form content in to ref of the data - remove validate\n         * form - send data change event\n         * @method onSaveForm\n         * @param {Object} event\n         */\n        onSaveForm: function (event) {\n            var isValid = event.data.self._validateItem($(event.currentTarget).parents('li.item').first(), event.data.self.metadataId);\n            if (isValid) {\n                event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                    id: event.data.self.metadataId\n                });\n            }\n            else {\n                $(event.currentTarget).parents('li.item').first().addClass('error');\n            }\n        },\n        /**\n         * In charge to close edit item without save\n         * @method onCloseForm\n         * @param {Object} event\n         */\n        onCloseForm: function (event) {\n            event.data.self._closeItem($(event.currentTarget).parents('li.item').first());\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: event.data.self.metadataId\n            });\n        },\n        /**\n         * In charge to delete item\n         * @method onDeleteItem\n         * @param {Object} event\n         */\n        onDeleteItem: function (event) {\n            var itemDataElement = $(event.currentTarget).parents('li.item').first();\n            var itemIdx = parseInt(itemDataElement.attr('class').match(/item-([0-9]+)/)[1]);\n            var formData = itemDataElement.find('form').first();\n            var itemData = formData.data('metadata');\n            itemData.tc = null;\n            itemData.tcin = null;\n            itemData.tcout = null;\n            itemData.label = null;\n            itemData.selected = false;\n            itemData.formCreated = false;\n            event.data.self.metadataManager.removeSelectedItem(itemIdx);\n            itemDataElement.remove();\n            itemData.deleted = true;\n            if (itemData.hasOwnProperty('subItem') && itemData.subItem === true) {\n                var data = event.data.self.mediaPlayer.getMetadataById(event.data.self.metadataId);\n                event.data.self.localisationManager.updateSpacialLocBlock(data);\n            }\n            itemDataElement.remove();\n            //Send data change event\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: event.data.self.metadataId\n            });\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectedMetadataChange: function (event, data) {\n            var listOfSelectedItemsElement = event.data.self.container.find('ul.listOfSelectedItems li.item');\n            for (var selectedItemElementIdx = 0;\n                 selectedItemElementIdx < listOfSelectedItemsElement.length;\n                 selectedItemElementIdx++) {\n                event.data.self._closeItem($(listOfSelectedItemsElement[selectedItemElementIdx]));\n            }\n            event.data.self.metadataId = data.metadataId !== null ? data.metadataId.toString() : null;\n            event.data.self.clearMessage();\n        },\n        /**\n         * In charge to add item\n         * @param {Object} event\n         */\n        onAddItem: function (event) {\n            if (event.data.self.metadataId !== null && event.data.self.metadataId !== '') {\n                var dataList = null;\n                var lineType = '';\n                if (event.data.self.metadataId !== null) {\n                    var metadataObject = event.data.self.mediaPlayer.getBlockMetadata(event.data.self.metadataId);\n                    var metadataDataType = (metadataObject !== null && metadataObject.hasOwnProperty('type')) ? metadataObject.type : '';\n                    lineType = event.data.self.bindingManager.getLinetypeWithDataType(metadataDataType);\n                }\n                switch (lineType) {\n                    case fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT:\n                        dataList = [\n                            {\n                                'tcin': event.data.self.mediaPlayer.getCurrentTime(),\n                                'tcout': Math.min(event.data.self.mediaPlayer.getCurrentTime() + (event.data.self.mediaPlayer.getDuration() * Math.min(event.data.self.settings.defaultPercentWidth, 1)), event.data.self.mediaPlayer.getDuration()),\n                                'label': 'New segment',\n                                'tclevel': 1,\n                                'selected': true,\n                                'formCreated': false\n                            }\n                        ];\n                        event.data.self.mediaPlayer.addMetadataById(event.data.self.metadataId, dataList);\n                        event.data.self.clearMessage();\n                        break;\n                    case fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_IMAGE:\n                        dataList = [\n                            {\n                                'tc': event.data.self.mediaPlayer.getCurrentTime(),\n                                'label': 'New Point',\n                                'tclevel': 1,\n                                'selected': true,\n                                'formCreated': false\n                            }\n                        ];\n                        event.data.self.mediaPlayer.addMetadataById(event.data.self.metadataId, dataList);\n                        event.data.self.clearMessage();\n                        break;\n                    case fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_CUEPOINT:\n                        dataList = [\n                            {\n                                'tc': event.data.self.mediaPlayer.getCurrentTime(),\n                                'label': 'New Point',\n                                'tclevel': 1,\n                                'selected': true,\n                                'formCreated': false\n                            }\n                        ];\n                        event.data.self.mediaPlayer.addMetadataById(event.data.self.metadataId, dataList);\n                        event.data.self.clearMessage();\n                        break;\n                    default:\n                        event.data.self.setMessage([\n                            fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA_ITEM_TYPE\n                        ], 'error');\n                }\n\n            }\n            else {\n                event.data.self.setMessage([\n                    fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA\n                ], 'error');\n            }\n\n        },\n        /**\n         * In charge to clear all selected items\n         * @param {Object} event\n         */\n        onClearItems: function (event) {\n            var listOfSelectedItemsElement = event.data.self.container.find('ul.listOfSelectedItems li.item');\n            for (var selectedItemElementIdx = 0;\n                 selectedItemElementIdx < listOfSelectedItemsElement.length;\n                 selectedItemElementIdx++) {\n                event.data.self._closeItem($(listOfSelectedItemsElement[selectedItemElementIdx]));\n            }\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: event.data.self.metadataId\n            });\n        },\n        /**\n         * In charge to clear all selected items\n         * @param {Object} event\n         */\n        onValidateItems: function (event) {\n            var listOfSelectedItemsElement = event.data.self.container.find('ul.listOfSelectedItems li.item');\n            for (var selectedItemElementIdx = 0;\n                 selectedItemElementIdx < listOfSelectedItemsElement.length;\n                 selectedItemElementIdx++) {\n                var isValid = event.data.self._validateItem($(listOfSelectedItemsElement[selectedItemElementIdx]));\n                if (!isValid) {\n                    $(listOfSelectedItemsElement[selectedItemElementIdx]).addClass('error');\n                }\n            }\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: event.data.self.metadataId\n            });\n        },\n        /**\n         * On data change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onDataChange: function (event, data) {\n            if (data.id === event.data.self.metadataId) {\n                event.data.self.updateFormItems();\n            }\n        },\n        /**\n         * Fired on selected items change\n         * @method onSelectedItemsChange\n         * @param {Object} event\n         */\n        onSelectedItemsChange: function (event) {\n            if (event.data.self.metadataId !== null && event.data.self.metadataId !== '') {\n                event.data.self.updateFormItems();\n                event.data.self.updateSelectedItems();\n            }\n\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"EndDataChange\");\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/editor/plugin-metadata-items-ref-editor.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage all items of metadata\n * @class MetadataItemsRefEditorPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-editor\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.MetadataItemsRefEditorPlugin\", {\n        classCss: 'ajs-plugin plugin-items-ref-editor',\n        classCssForThumbCapture: 'ajs-icon ajs-icon-screenshot',\n        classCssEditForm: 'ajs-icon ajs-icon-check',\n        LABEL_DESCRIPTION: 'Description'\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        metadataId: null,\n        selectedItem: null,\n        /**\n         * Localisation manager\n         * @property localisationManager\n         * @type {Object}\n         * @default null\n         */\n        localisationManager: null,\n        /**\n         * Initialize\n         * @method initialize\n         */\n        initialize: function () {\n            this.metadataId = null;\n            this.selectedItem = null;\n            this._super();\n            this.settings = $.extend({\n                defaultPercentWidth: 0.1,\n                timeFormat: 'f',\n                framerate: this.settings.framerate\n            }, this.settings.parameters || {});\n\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.localisationManager = new fr.ina.amalia.player.LocalisationManager();\n            this.pluginContainer.append(this.container);\n            this.createMetadataItemsBlock();\n            this.defineListeners();\n        },\n        /**\n         * Create metadata list block\n         * @method createHeaderElement\n         */\n        createMetadataItemsBlock: function () {\n            var titleElement = $('<div>', {\n                'class': 'heading off'\n            });\n            titleElement.append(\"<p class='title'></p>\");\n\n            var element = $('<div>', {\n                'class': 'body'\n            });\n            var messagesElement = $('<div>', {\n                'class': 'messages-container'\n            }).hide();\n            var listOfItemsElement = $('<ul>', {\n                'class': 'listOfSelectedItems'\n            });\n            element.append(titleElement);\n            element.append(messagesElement);\n            element.append(listOfItemsElement);\n            this.container.append(element);\n        },\n        createItem: function (data) {\n            var itemElement = $('<li>', {\n                'class': 'item'\n            }).data('metadata', data);\n\n            var thumbElement = $('<div>', {\n                'class': 'thumb'\n            });\n            var captureElement = $('<span>', {\n                \"class\": \"capture \" + this.Class.classCssForThumbCapture\n            });\n            if (data.hasOwnProperty('thumb') && data.thumb !== null && data.thumb !== \"\") {\n                thumbElement.css('background-image', 'url(' + data.thumb + ')');\n            }\n            thumbElement.append(captureElement);\n            itemElement.append(thumbElement);\n            var labelTextElement = $('<span>', {\n                'class': 'text',\n                'text': data.hasOwnProperty('label') ? data.label : ''\n            });\n            itemElement.append(labelTextElement);\n            var tcInTextElement = $('<span>', {\n                'class': 'tcin',\n                'text': data.hasOwnProperty('tcin') ? fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcin, this.settings.framerate, this.settings.timeFormat) : ''\n            });\n            itemElement.append(tcInTextElement);\n            var tcOutTextElement = $('<span>', {\n                'class': 'tcout',\n                'text': data.hasOwnProperty('tcout') ? fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(data.tcout, this.settings.framerate, this.settings.timeFormat) : ''\n            });\n            itemElement.append(tcOutTextElement);\n            itemElement.append(\"<div style='clear: both;'></div>\");\n            //Add data fields\n            var editDataElements = $('<div>', {\n                'class': 'edit-form'\n            });\n            var textElement = $('<p>', {class: 'text-element'});\n            var textElementLabel = $('<label>', {'class': 'text-label', 'text': this.Class.LABEL_DESCRIPTION});\n            var text = (data !== null && data.hasOwnProperty('data') && data.data !== null && data.data.hasOwnProperty('text') && data.data.text !== null) ? data.data.text.join('\\n') : '';\n            var textContainer = $('<textarea>', {class: 'text-element', text: text});\n            textElement.append(textElementLabel);\n            textElement.append(textContainer);\n            editDataElements.append(textElement);\n            textContainer.one('keydown', function () {\n                itemElement.addClass('change');\n            });\n            var actionsElements = $('<div>', {\n                'class': 'actions-list'\n            });\n            var saveElement = $('<button>', {\n                'class': 'save ' + this.Class.classCssEditForm\n            });\n            actionsElements.append(saveElement);\n            editDataElements.append(actionsElements);\n            itemElement.append(editDataElements);\n            return itemElement;\n        },\n\n        /**\n         * In charge to update metadata block\n         */\n        updataFormHeader: function () {\n            var metadataBlock = this.mediaPlayer.getBlockMetadata(this.metadataId);\n            if (metadataBlock !== null && typeof metadataBlock === \"object\" && metadataBlock.hasOwnProperty('label')) {\n                this.container.find('div.heading').removeClass('off').addClass('on');\n                this.container.find('div.heading p.title').html(metadataBlock.label);\n            }\n            else {\n                this.container.find('div.heading').removeClass('on').addClass('off');\n            }\n        },\n        /**\n         * Update form items\n         */\n        updateFormItems: function () {\n            var listOfSelectedItemsElement = this.container.find('ul.listOfSelectedItems');\n            listOfSelectedItemsElement.empty();\n            this.localisationManager.updateLocBlock(this.mediaPlayer.getMetadataById(this.metadataId));\n            var listOfMetadata = this.mediaPlayer.getMetadataById(this.metadataId);\n            if (listOfMetadata !== null) {\n                for (var i = 0;\n                     i < listOfMetadata.length;\n                     i++) {\n                    var data = listOfMetadata[i];\n                    if (data !== null && data.hasOwnProperty('tcin') && data.tcin >= this.mediaPlayer.getTcOffset()) {\n                        listOfSelectedItemsElement.append(this.createItem(data));\n                    }\n                }\n                listOfSelectedItemsElement.find('li.item .thumb .capture').on('click', {\n                        self: this\n                    },\n                    this.onClickToCapture);\n                //add on click item for seek to item tcin\n                listOfSelectedItemsElement.find('li.item').on('click', {\n                        self: this\n                    },\n                    this.onSelectItem);\n                listOfSelectedItemsElement.find('li.item .edit-form .save').on('click', {\n                        self: this\n                    },\n                    this.onSaveItem);\n            }\n        },\n\n        updateCaptureImage: function (element) {\n            var thumb = this.mediaPlayer.getCurrentImage();\n            var metadata = element.data(\"metadata\");\n            if (typeof metadata !== \"undefined\") {\n                metadata.thumb = thumb;\n                element.find('.thumb').css('background-image', \"url(\" + thumb + \")\");\n            }\n        },\n        saveItem: function (element) {\n            var texts = element.find('.edit-form textarea.text-element').val().split('\\n');\n            var metadata = element.data(\"metadata\");\n            if (typeof metadata !== \"undefined\") {\n                if (metadata.data === null || typeof metadata.data === \"undefined\") {\n                    metadata.data = {};\n                }\n                metadata.data.text = texts;\n            }\n            element.removeClass('change');\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                self: this\n            }, this.onDataChange);\n            //On select metadata\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * Fired on data change event\n         * @method onDataChange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onDataChange: function (event, data) {\n            if (event.data.self.metadataId !== null && event.data.self.metadataId === data.id) {\n                event.data.self.updataFormHeader();\n                event.data.self.updateFormItems();\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectedItemsChange\");\n                }\n            }\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectedMetadataChange: function (event, data) {\n            event.data.self.metadataId = data.metadataId !== null ? data.metadataId.toString() : null;\n            event.data.self.updataFormHeader();\n            event.data.self.updateFormItems();\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectedMetadataChange:\" + data.metadataId);\n            }\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectItem\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectItem: function (event) {\n            event.data.self.container.find('ul.listOfSelectedItems').find('li.item').removeClass('on');\n            $(event.currentTarget).addClass('on');\n            var metadata = $(event.currentTarget).data('metadata');\n            if (metadata.hasOwnProperty('tcin') === true && metadata.tcin !== null) {\n                event.data.self.mediaPlayer.setCurrentTime(parseFloat(metadata.tcin));\n            }\n            else if (metadata.hasOwnProperty('tc') === true && metadata.tc !== null) {\n                event.data.self.mediaPlayer.setCurrentTime(parseFloat(metadata.tc));\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectItem\");\n            }\n        },\n        /**\n         * Fired on click to capture icon\n         * @method onClickToCapture\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onClickToCapture: function (event) {\n            event.data.self.updateCaptureImage($(event.currentTarget).parents('li').first());\n            event.preventDefault();\n            event.stopPropagation();\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickToCapture\");\n            }\n        },\n        /**\n         * Fired on click to save item\n         * @method onSaveItem\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSaveItem: function (event) {\n            event.preventDefault();\n            event.data.self.saveItem($(event.currentTarget).parents('li').first());\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickToCapture\");\n            }\n        }\n    });\n\n"
  },
  {
    "path": "src/plugins/editor/plugin-metadata-list-editor.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Blocks metadata editor charge to list, add and delete blocks\n * @class MetadataListEditorPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-editor\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.MetadataListEditorPlugin\", {\n        classCss: \"ajs-plugin editor plugin-list-editor\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * true if load data started anw\n         * @property loadDataStarted\n         * @default null\n         */\n        loadDataStarted: false,\n        /**\n         * Instance of metadata manager\n         * @property metadataManager\n         * @default null\n         */\n        metadataManager: null,\n        /**\n         * Initialize editor plugin and create container of this plugin\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.settings = $.extend({\n                    defaultDataType: 'default',\n                    defaultAuthor: 'amalia.js',\n                    defaultColor: '#2196f3',\n                    defaultShape: 'circle'\n                },\n                this.settings.parameters || {});\n\n            this.container = $('<div>', {\n                'class': this.Class.classCss\n            });\n            this.pluginContainer.append(this.container);\n            this.createHeaderElement();\n            this.createMetadataListBlock();\n            this.createFooterElement();\n            this.defineListeners();\n            this.metadataManager = this.mediaPlayer.getMetadataManager();\n            this.loaderContainer.show();\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE, {\n                self: this\n            }, this.onBeginDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                self: this\n            }, this.onDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE, {\n                self: this\n            }, this.onEndDataChange);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * Create block header\n         * @method createHeaderElement\n         */\n        createHeaderElement: function () {\n            var element = $('<div>', {\n                'class': 'heading'\n            });\n            var titleElement = $('<h3>', {\n                'class': 'title',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_LIST_EDITOR_LABEL_HEADER\n            });\n            element.append(titleElement);\n            this.container.append(element);\n        },\n        /**\n         * Create metadata list block\n         * @method createHeaderElement\n         */\n        createMetadataListBlock: function () {\n            var element = $('<div>', {\n                'class': 'body'\n            });\n            var list = $('<ul>', {\n                'class': 'listOfmetadata'\n            });\n\n            element.append(list);\n            this.container.append(element);\n        },\n        /**\n         * Create block header\n         * @method createHeaderElement\n         */\n        createFooterElement: function () {\n            var element = $('<div>', {\n                'class': 'footer'\n            });\n            var btnAddMetadata = $('<button>', {\n                'class': 'add-metadata ajs-icon ajs-icon-plus'\n            }).on('click', {\n                self: this\n            }, this.onAddMetadata);\n            element.append(btnAddMetadata);\n            this.container.append(element);\n        },\n        /**\n         * In charge to update list of metadata\n         */\n        updateMetadataListBlock: function () {\n            var list = this.container.find('ul.listOfmetadata');\n            // clean old list\n            list.empty();\n            var listOfMetadata = this.mediaPlayer.getBlocksMetadata();\n\n            if (listOfMetadata !== null) {\n                for (var key in listOfMetadata) {\n                    var item = $('<li>', {\n                        'class': 'item item-' + key,\n                        'data-metadata-id': key\n                    });\n                    var textElement = $('<span>', {\n                        'class': 'text',\n                        'text': key\n                    });\n                    var deleteElement = $('<span>', {\n                        'class': 'delete ajs-icon ajs-icon-remove'\n                    });\n\n                    var duplicateElement = $('<span>', {\n                        'class': 'duplicate ajs-icon ajs-icon-reorder'\n                    });\n\n                    item.append(textElement);\n                    item.append(deleteElement);\n                    item.append(duplicateElement);\n                    list.append(item);\n                }\n            }\n            list.find('li').on('click', {\n                self: this\n            }, this.onSelectMetadata);\n            var selectedMetadataId = this.mediaPlayer.getSelectedMetadataId();\n            list.find('li.item').removeClass('selected');\n            if (selectedMetadataId !== null) {\n                list.find('li.item.item-' + selectedMetadataId).addClass('selected');\n            }\n            this.loaderContainer.hide();\n        },\n        /**\n         * Set selected metadata id in the instance of player\n         * @param metadataId identification\n         */\n        setSelectedMetadataId: function (metadataId) {\n            if (this.mediaPlayer.getSelectedMetadataId() !== metadataId) {\n                this.mediaPlayer.setSelectedMetadataId(metadataId);\n            }\n        },\n        /**\n         * For remove metadata\n         * @param metadataId identification\n         */\n        removeMetadataById: function (metadataId) {\n            this.mediaPlayer.deleteAllMetadataById(metadataId);\n            this.updateMetadataListBlock();\n        },\n        /**\n         * In charge to duplicate block data\n         * @param oldMetadataId identification\n         */\n        duplicateBlock: function (oldMetadataId) {\n            var newMetadataId = '_' + fr.ina.amalia.player.helpers.UtilitiesHelper.generateUUID();\n            var dupArray = JSON.parse(JSON.stringify(this.mediaPlayer.getMetadataById(oldMetadataId)));\n            var blockMetadata = this.metadataManager.getBlockMetadata(oldMetadataId);\n            this.metadataManager.updateBlockMetadata(newMetadataId, {\n                id: newMetadataId,\n                label: newMetadataId,\n                type: (blockMetadata.hasOwnProperty('type')) ? blockMetadata.type : this.settings.defaultDataType,\n                author: (blockMetadata.hasOwnProperty('author')) ? blockMetadata.type : this.settings.defaultAuthor,\n                color: (blockMetadata.hasOwnProperty('color')) ? blockMetadata.type : this.settings.defaultColor,\n                shape: (blockMetadata.hasOwnProperty('shape')) ? blockMetadata.type : this.settings.defaultShape\n            });\n            this.mediaPlayer.setMetadataById(newMetadataId, dupArray);\n            this.mediaPlayer.setSelectedMetadataId(newMetadataId);\n            this.updateMetadataListBlock();\n        },\n        /**\n         * For add metadata\n         * @param e event\n         */\n        onAddMetadata: function (e) {\n            var metadataId = '_' + fr.ina.amalia.player.helpers.UtilitiesHelper.generateUUID();\n            e.data.self.metadataManager.updateBlockMetadata(metadataId, {\n                id: metadataId,\n                label: metadataId,\n                type: e.data.self.settings.defaultDataType,\n                author: e.data.self.settings.defaultAuthor,\n                color: e.data.self.settings.defaultColor,\n                shape: e.data.self.settings.defaultShape\n            });\n            e.data.self.mediaPlayer.addMetadataById(metadataId, []);\n            e.data.self.mediaPlayer.setSelectedMetadataId(metadataId);\n            e.data.self.updateMetadataListBlock();\n        },\n        /**\n         * Fired on select metadata\n         * @method onSelectMetadata\n         * @param e event\n         */\n        onSelectMetadata: function (e) {\n            var metadataId = $(e.currentTarget).attr('data-metadata-id');\n            var targetElement = $(e.target);\n            if (targetElement.hasClass('delete')) {\n                e.data.self.removeMetadataById(metadataId);\n            }\n\n            else if (targetElement.hasClass('duplicate')) {\n                e.data.self.duplicateBlock(metadataId);\n            }\n            else {\n                e.data.self.setSelectedMetadataId(metadataId);\n            }\n\n            if (e.data.self.logger !== null) {\n                e.data.self.logger.trace(e.data.self.Class.fullName, \"onSelectMetadata  : \" + metadataId);\n            }\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         * @param {Object} e\n         * @param {Object} data\n         */\n        onSelectedMetadataChange: function (e, data) {\n            e.data.self.container.find('ul.listOfmetadata .item').removeClass('selected');\n            if (data.metadataId !== null) {\n                e.data.self.container.find('ul.listOfmetadata [data-metadata-id=\"' + data.metadataId + '\"]').addClass('selected');\n                var movePos = e.data.self.container.find('ul.listOfmetadata [data-metadata-id=\"' + data.metadataId + '\"]').offset();\n                if (typeof movePos === 'object' && movePos.hasOwnProperty('top')) {\n                    e.data.self.container.find('ul.listOfmetadata').stop().animate({\n                            scrollTop: movePos.top\n                        },\n                        500, 'easeInOutExpo');\n                }\n            }\n        },\n        /**\n         * Fired on begin data change event\n         * @method onBeginDataChange\n         * @param {Object} event\n         */\n        onBeginDataChange: function (event) {\n            event.data.self.loadDataStarted = true;\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onBeginDataChange\");\n            }\n        },\n        /**\n         * Fired on data change event\n         * @param {Object} event\n         */\n        onDataChange: function (event) {\n            if (event.data.self.loadDataStarted === false) {\n                event.data.self.updateMetadataListBlock();\n            }\n        },\n        /**\n         * Fired on end data change event\n         * @method onEndDataChange\n         * @param {Object} event\n         */\n        onEndDataChange: function (event) {\n            event.data.self.loadDataStarted = false;\n            event.data.self.updateMetadataListBlock();\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"EndDataChange\");\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/overlay/draw-base.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class for draw component\n * @class DrawBase\n * @namespace fr.ina.amalia.player.plugins.overlay\n * @module plugin\n * @submodule plugin-overlay\n * @constructor\n * @param {Object} settings Defines the configuration of this class\n * @param {Object} mediaPlayer\n * @param {Object} data\n */\n$.Class(\"fr.ina.amalia.player.plugins.overlay.DrawBase\", {\n        classCss: \"draw-base\",\n        eventTypes: {\n            CLICK: \"fr.ina.amalia.player.plugins.overlay.DrawBase.eventTypes.CLICK\"\n        }\n    },\n    {\n        /**\n         * Defines configuration\n         * @property settings\n         * @type {Object}\n         * @default {}\n         */\n        settings: {},\n        /**\n         * In charge to render messages in the web console output\n         * @property logger\n         * @type {Object}\n         * @default null\n         */\n        logger: null,\n        /**\n         * In charge to render messages in the web console output\n         * @property container\n         * @type {Object}\n         * @default null\n         */\n        container: null,\n        /**\n         * In charge to render messages in the web console output\n         * @property mediaPlayer\n         * @type {Object}\n         * @default null\n         */\n        mediaPlayer: null,\n        /**\n         * Component container element\n         * @property element\n         * @type {Object}\n         * @default null\n         */\n        element: null,\n        /**\n         * Label container element\n         * @property labelElement\n         * @type {Object}\n         * @default null\n         */\n        labelElement: null,\n        /**\n         * Spatial data\n         * @property data\n         * @type {Object}\n         * @default null\n         */\n        data: null,\n        /**\n         * Main canvas paper\n         * @property paper\n         * @type {Object}\n         * @default null\n         */\n        paper: null,\n        /**\n         * Main canvas paper\n         * @property shape\n         * @type {Object}\n         * @default null\n         */\n        shape: null,\n        /**\n         * Localisation manager\n         * @property localisationManager\n         * @type {Object}\n         * @default null\n         */\n        localisationManager: null,\n        /**\n         * overlay manager\n         * @property overlayManager\n         * @type {Object}\n         * @default null\n         */\n        overlayManager: null,\n        /**\n         * Init\n         * @constructor\n         * @param {Object} settings\n         * @param {Object} mediaPlayer\n         * @param {Object} data\n         */\n        init: function (settings, mediaPlayer, data, overlayManager) {\n            this.mediaPlayer = mediaPlayer;\n            this.data = data;\n            this.overlayManager = overlayManager;\n            this.localisationManager = new fr.ina.amalia.player.LocalisationManager();\n            this.settings = $.extend({\n                    debug: false,\n                    container: null,\n                    canvas: null,\n                    demo: false,\n                    editable: false,\n                    metadataId: '',\n                    style: {\n                        'fill': \"#00CCFF\",\n                        'strokeWidth': 1,\n                        'stroke': '#000',\n                        'fillOpacity': 0.0,\n                        'strokeDasharray': \"- \"\n                    },\n                    labelStyle: {\n                        font: \"12px Arial\",\n                        opacity: 1,\n                        fill: \"#00CCFF\",\n                        strokeWidth: 1,\n                        stroke: '#000',\n                        fillOpacity: 0.0\n                    }\n                },\n                settings || {});\n            this.paper = this.settings.canvas;\n            this.container = this.settings.container;\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            if (typeof this.settings.canvas === \"object\" && this.data !== null) {\n                this.initialize();\n            }\n            if (this.mediaPlayer !== null) {\n                var mainContainer = this.mediaPlayer.getContainer();\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.PLAYING, {\n                    self: this\n                }, this.onPlay);\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.PAUSED, {\n                    self: this\n                }, this.onPause);\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.SEEK, {\n                    self: this\n                }, this.onSeek);\n            }\n        },\n        /**\n         * Initialize\n         * @method initialize\n         */\n        initialize: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n                this.logger.info(this.data);\n            }\n        },\n        /**\n         * Return default style component\n         * @method getStyle\n         * @return {Object}\n         */\n        getStyle: function () {\n\n            return {\n                'fill': this.settings.style.fill,\n                'stroke': (this.data.hasOwnProperty('color') && this.data.color !== \"\" && typeof this.data.color !== \"undefined\") ? this.data.color : this.settings.style.stroke,\n                'stroke-width': this.settings.style.strokeWidth,\n                'fill-opacity': this.settings.style.fillOpacity,\n                'stroke-dasharray': this.settings.style.strokeDasharray\n            };\n        },\n        /**\n         * Return default style component\n         * @method getLabelStyle\n         * @return {Object}\n         */\n        getLabelStyle: function () {\n            return {\n                'font': this.settings.labelStyle.font,\n                'opacity': this.settings.labelStyle.opacity,\n                'fill': this.settings.labelStyle.fill,\n                'stroke': this.settings.labelStyle.stroke,\n                'stroke-width': this.settings.labelStyle.strokeWidth,\n                'fill-opacity': this.settings.labelStyle.fillOpacity\n            };\n        },\n        /**\n         * In charge to plug free transform object\n         * @returns {undefined}\n         */\n        plugFreeTransformObject: function () {\n            if (this.element.attrs !== null) {\n                var transformConfig = {\n                    keepRatio: false,\n                    fill: 'black',\n                    draw: [\n                        'bbox',\n                        'circle'\n                    ],\n                    range: {\n                        scale: [\n                            0,\n                            99999\n                        ]\n                    }\n                };\n                var _ft = this.settings.canvas.freeTransform(this.element, transformConfig, this.onTransformationCallback);\n                _ft.self = this;\n            }\n        },\n        /**\n         * In charge to plug free transform object\n         * @returns {undefined}\n         */\n        unplugFreeTransformObject: function () {\n            if (this.element.hasOwnProperty('freeTransform') && this.element.freeTransform !== null) {\n                this.element.freeTransform.unplug();\n            }\n        },\n        /**\n         * In charge to update shape position\n         * @param {Object} shape\n         * @param {Object} shapePos\n         */\n        updatePosShape: function (shapePos) {\n            var _player = this.mediaPlayer;\n            var tc = _player.getCurrentTime();\n            if (this.data.hasOwnProperty('refLoc') && typeof this.data.refLoc === \"object\" && this.data.refLoc.tc !== tc) {\n                if (this.data.refLoc.hasOwnProperty('sublocalisations') === false || this.data.refLoc.sublocalisations === null || typeof this.data.refLoc.sublocalisations !== \"object\") {\n                    //init sublocalisation\n                    this.data.refLoc.sublocalisations = {\n                        localisation: []\n                    };\n                    var oldTc = parseFloat(this.data.refLoc.tc);\n                    var firstShapePos = jQuery.extend({}, {\n                        tc: oldTc,\n                        shape: this.data.refLoc.shape,\n                        tclevel: 1\n\n                    });\n                    this.data.refLoc.sublocalisations.localisation.push(firstShapePos);\n                    this.data.refLoc.shape = null;\n                    if (oldTc < tc) {\n                        this.data.refLoc.tcin = oldTc;\n                        this.data.refLoc.tc = oldTc;\n                        this.data.refLoc.tcout = tc;\n                    }\n                    else {\n                        this.data.refLoc.tcin = tc;\n                        this.data.refLoc.tc = tc;\n                        this.data.refLoc.tcout = oldTc;\n                    }\n                }\n                //search if localisations pos for this tc\n                var duplicateItem = $.grep(this.data.refLoc.sublocalisations.localisation, function (a) {\n                    return a.tc === tc;\n                });\n                if (duplicateItem.length > 0) {\n                    duplicateItem[0].shape = shapePos;\n                }\n                else {\n                    // add new shap pos\n                    this.data.refLoc.sublocalisations.localisation.push({\n                        tc: tc,\n                        shape: shapePos,\n                        tclevel: 1\n                    });\n                }\n\n                _player.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                    id: this.data.hasOwnProperty('metadataId') ? this.data.metadataId : _player.getSelectedMetadataId()\n                });\n            }\n            else if (this.data.hasOwnProperty('refLoc') && typeof this.data.refLoc === \"object\") {\n                this.data.refLoc.shape = shapePos;\n                _player.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                    id: this.data.hasOwnProperty('metadataId') ? this.data.metadataId : _player.getSelectedMetadataId()\n                });\n            }\n        },\n        /**\n         * Fired on click event\n         * @method onClick\n         * @param {Object} event\n         */\n        onClick: function (event) {\n            if (event.data.self.container !== null) {\n                event.data.self.container.trigger(fr.ina.amalia.player.plugins.overlay.DrawBase.eventTypes.CLICK, {\n                    'tcin': event.data.tcin,\n                    'tcout': event.data.tcout,\n                    'data': event.data.data\n                });\n            }\n        },\n        /**\n         * Fired on play event\n         * @method onPlay\n         * @param {Object} event\n         */\n        onPlay: function (event) {\n            if (event.data.self.element !== null) {\n                if (event.data.self.settings.editable === true) {\n                    event.data.self.unplugFreeTransformObject();\n                }\n                event.data.self.element.resume();\n                if (event.data.self.labelElement !== null) {\n                    event.data.self.labelElement.resume();\n                }\n            }\n        },\n        /**\n         * Fired on pause event\n         * @method onPause\n         * @param {Object} event\n         */\n        onPause: function (event) {\n            if (event.data.self.element !== null) {\n                event.data.self.element.pause();\n                if (event.data.self.labelElement !== null) {\n                    event.data.self.labelElement.pause();\n                }\n                if (event.data.self.settings.editable === true) {\n                    event.data.self.unplugFreeTransformObject();\n                    event.data.self.plugFreeTransformObject();\n                }\n            }\n        },\n        /**\n         * Fired on seek event to clear canvas\n         * @method onSeek\n         * @param {Object} event\n         */\n        onSeek: function (event) {\n            if (event.data.self.settings.editable === true) {\n                event.data.self.unplugFreeTransformObject();\n            }\n            event.data.self.element.remove();\n            if (event.data.self.labelElement !== null) {\n                event.data.self.labelElement.remove();\n            }\n        },\n        /**\n         * Fired when an animation was complete.\n         * @method onEndOfAnimation\n         */\n        onEndOfAnimation: function () {\n            if (this.data.hasOwnProperty('self') && this.data.self.settings.editable === true) {\n                this.data('self').unplugFreeTransformObject();\n            }\n            if (this.data('demo') !== true) {\n                this.remove();\n            }\n        },\n        /**\n         * Call when end transformation\n         * @param {type} ft\n         * @param {type} events\n         */\n        onTransformationCallback: function (ft, events) {\n            if ($.inArray('init', events) === -1 && ft.self.overlayManager.getEraseState() === true) {\n                if (ft.self.data.refLoc.hasOwnProperty('sublocalisations') && ft.self.data.refLoc.sublocalisations !== null && ft.self.data.refLoc.sublocalisations.hasOwnProperty('localisation') && ft.self.data.refLoc.sublocalisations.localisation !== null) {\n                    var localisations = ft.self.data.refLoc.sublocalisations.localisation;\n                    for (var i = 0; i < localisations.length; i++) {\n                        var loc = localisations[i];\n                        if (loc.tc === ft.self.data.tcout) {\n                            localisations.splice(i, 1);\n                        }\n                    }\n                }\n                else {\n                    ft.self.data.refLoc.delete = true;\n                    ft.self.data.refLoc.tc = null;\n                    ft.self.data.refLoc.tcin = null;\n                }\n                ft.unplug();\n                ft.subject.remove();\n                ft.self.localisationManager.updateLocBlock(ft.self.mediaPlayer.getMetadataById(ft.self.data.metadataId));\n                ft.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                    id: ft.self.data.hasOwnProperty('metadataId') ? ft.self.data.metadataId : ft.self.mediaPlayer.getSelectedMetadataId()\n                });\n                ft.self.overlayManager.setEraseState(false);\n            } else if ($.inArray('drag end', events) > -1 || $.inArray('scale end', events) > -1) {\n                //translate pos is a center object\n                var _shapePos = {\n                    c: {\n                        x: parseFloat((ft.attrs.center.x + ft.attrs.translate.x) / ft.self.paper.width),\n                        y: parseFloat((ft.attrs.center.y + ft.attrs.translate.y) / ft.self.paper.height)\n                    },\n                    rx: parseFloat((ft.attrs.size.x * ft.attrs.scale.x) / 2 / ft.self.paper.width),\n                    ry: parseFloat((ft.attrs.size.y * ft.attrs.scale.y) / 2 / ft.self.paper.height),\n                    o: parseFloat(ft.attrs.rotate),\n                    t: ft.self.shape\n                };\n                ft.self.updatePosShape(_shapePos);\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/overlay/draw-ellipse.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * In charge to draw ellipse shape and extend draw base class\r\n * @class DrawEllipse\r\n * @namespace fr.ina.amalia.player.plugins.overlay\r\n * @module plugin\r\n * @submodule plugin-overlay\r\n * @extends fr.ina.amalia.player.plugins.overlay\r\n * @constructor\r\n * @param {Object} settings Defines the configuration of this class\r\n * @param {Object} mediaPlayer\r\n * @param {Object} data\r\n */\r\nfr.ina.amalia.player.plugins.overlay.DrawBase.extend(\"fr.ina.amalia.player.plugins.overlay.DrawEllipse\", {\r\n        classCss: \"spatial-ellipse\"\r\n\r\n    },\r\n    {\r\n        /**\r\n         * Initialize\r\n         * @method initialize\r\n         */\r\n        initialize: function () {\r\n            //set shape name\r\n            this.shape = 'ellipse';\r\n            if (this.data.hasOwnProperty('tcin') && this.data.hasOwnProperty('tcout') && this.data.hasOwnProperty('start') && this.data.hasOwnProperty('end')) {\r\n                var style = this.getStyle();\r\n                var startCor = this.getEllipseData(this.data.start);\r\n                var endCor = this.getEllipseData(this.data.end);\r\n                var duration = parseFloat(this.data.tcout - this.mediaPlayer.getCurrentTime());\r\n                var label = this.data.hasOwnProperty('label') ? this.data.label : '';\r\n                if (startCor !== null && endCor !== null) {\r\n                    this.element = this.settings.canvas.ellipse(startCor.cx, startCor.cy, startCor.rx, startCor.ry);\r\n                    // Set style\r\n                    this.element.attr(style);\r\n                    this.element.transform(\"r\" + Math.round((startCor.o / Math.PI) * 180));\r\n                    this.element.attr({\r\n                        'cursor': 'pointer'\r\n                    });\r\n                    // set label\r\n                    if (this.settings.labels === true && label !== '') {\r\n                        this.labelElement = this.settings.canvas.text(startCor.cx, startCor.cy, label).attr(this.getLabelStyle());\r\n                    }\r\n\r\n                    // end pos\r\n                    var elattrs = {\r\n                        cx: endCor.cx,\r\n                        cy: endCor.cy,\r\n                        rx: endCor.rx,\r\n                        ry: endCor.ry,\r\n                        transform: 'r' + Math.round((endCor.o / Math.PI) * 180)\r\n                    };\r\n                    // duration en ms\r\n                    this.element.stop().animate(elattrs, duration * 1000, \"\", this.onEndOfAnimation);\r\n                    if (this.labelElement !== null) {\r\n                        // end pos\r\n                        var labelEndCor = {\r\n                            x: endCor.cx,\r\n                            y: endCor.cy\r\n                        };\r\n                        this.labelElement.stop().animate(labelEndCor, duration * 1000, \"\", this.onEndOfAnimation);\r\n                    }\r\n                    //pause animation\r\n                    if (this.mediaPlayer.isPaused()) {\r\n                        this.element.pause();\r\n                    }\r\n                    $(this.element.node).on('click', {\r\n                            'self': this,\r\n                            'data': this.data,\r\n                            'tcin': this.data.tcin,\r\n                            'tcout': this.data.tcout\r\n                        },\r\n                        this.onClick);\r\n                    return true;\r\n                }\r\n            }\r\n\r\n            if (this.logger !== null) {\r\n                this.logger.error(\"Error :\" + this.Class.fullName + \":initialize\");\r\n                this.logger.error(this.data);\r\n            }\r\n\r\n        },\r\n        /**\r\n         * In charge to set size and position\r\n         * @param {Object} data\r\n         * @return {Object}\r\n         */\r\n        getEllipseData: function (data) {\r\n            var ellipseParameter = {\r\n                // x coordinate of the centre\r\n                x: 0,\r\n                // y coordinate of the centre\r\n                y: 0,\r\n                // horizontal radius\r\n                rx: 0,\r\n                // vertical radius\r\n                ry: 0,\r\n                o: 0\r\n            };\r\n            try {\r\n                var width = this.settings.canvas.width;\r\n                var height = this.settings.canvas.height;\r\n                if (data.hasOwnProperty('shape')) {\r\n                    ellipseParameter.cx = data.shape.c.x * width;\r\n                    ellipseParameter.cy = data.shape.c.y * height;\r\n                    // horizontal radius\r\n                    ellipseParameter.rx = data.shape.rx * width;\r\n                    // vertical radius\r\n                    ellipseParameter.ry = data.shape.ry * height;\r\n                    ellipseParameter.o = data.shape.o;\r\n                    return ellipseParameter;\r\n                }\r\n            }\r\n            catch (e) {\r\n                if (this.logger !== null) {\r\n                    this.logger.error(\"Error to parse data getEllipseData\");\r\n                    this.logger.error(data);\r\n                }\r\n            }\r\n            return null;\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/plugins/overlay/draw-rect.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to manage rectangle shape and extend draw base class\n * @class DrawRect\n * @namespace fr.ina.amalia.player.plugins.overlay\n * @module plugin\n * @submodule plugin-overlay\n * @extends fr.ina.amalia.player.plugins.overlay\n * @constructor\n * @param {Object} settings Defines the configuration of this class\n * @param {Object} mediaPlayer\n * @param {Object} data\n */\nfr.ina.amalia.player.plugins.overlay.DrawBase.extend(\"fr.ina.amalia.player.plugins.overlay.DrawRect\", {\n        classCss: \"spatial-base\"\n\n    },\n    {\n        /**\n         * Initialize component\n         * @method initialize\n         */\n        initialize: function () {\n            this.shape = 'rectangle';\n            if (this.data.hasOwnProperty('tcin') && this.data.hasOwnProperty('tcout') && this.data.hasOwnProperty('start') && this.data.hasOwnProperty('end')) {\n                var style = this.getStyle();\n                var startCor = this.getRectData(this.data.start);\n                var endCor = this.getRectData(this.data.end);\n                var duration = parseFloat(this.data.tcout - this.mediaPlayer.getCurrentTime());\n                var label = this.data.hasOwnProperty('label') ? this.data.label : '';\n                if (startCor !== null && endCor !== null) {\n                    // Rect\n                    this.element = this.settings.canvas.rect(startCor.x, startCor.y, startCor.w, startCor.h);\n                    // style\n                    this.element.attr(style);\n                    this.element.transform(\"r\" + Math.round((startCor.o / Math.PI) * 180));\n                    this.element.attr({\n                        'cursor': 'pointer'\n                    });\n                    this.element.data('demo', this.settings.demo);\n                    this.element.data('self', this);\n                    // Label\n                    if (this.settings.labels === true && label !== '') {\n                        this.labelElement = this.settings.canvas.text(startCor.x, startCor.y, label).attr(this.getLabelStyle());\n                    }\n                    // end pos\n                    var elementEndCor = {\n                        x: endCor.x,\n                        y: endCor.y,\n                        width: endCor.w,\n                        height: endCor.h,\n                        transform: 'r' + Math.round((endCor.o / Math.PI) * 180)\n                    };\n                    // duration en ms\n                    this.element.stop().animate(elementEndCor, duration * 1000, \"\", this.onEndOfAnimation);\n                    if (this.labelElement !== null) {\n                        // end pos\n                        var labelEndCor = {\n                            x: endCor.x,\n                            y: endCor.y\n                        };\n                        this.labelElement.stop().animate(labelEndCor, duration * 1000, \"\", this.onEndOfAnimation);\n                    }\n                    //pause animation\n                    if (this.mediaPlayer.isPaused()) {\n                        this.element.pause();\n                        if (this.labelElement !== null) {\n                            this.labelElement.pause();\n                        }\n                    }\n                    if (this.settings.editable === true && this.mediaPlayer.isPaused()) {\n                        this.plugFreeTransformObject();\n                    }\n\n                    $(this.element.node).on('click', {\n                            'self': this,\n                            'data': this.data,\n                            'tcin': this.data.tcin,\n                            'tcout': this.data.tcout\n                        },\n                        this.onClick);\n                    return true;\n                }\n            }\n\n            if (this.logger !== null) {\n                this.logger.error(\"Error :\" + this.Class.fullName + \":initialize\");\n                this.logger.error(this.data);\n            }\n        },\n        /**\n         * In charge to set rectangle size and position\n         * @param {Object} data\n         * @method getRectData\n         */\n        getRectData: function (data) {\n            var rectParameter = {\n                // x coordinate of the top left corner\n                x: 0,\n                // y coordinate of the top left corner\n                y: 0,\n                w: 0,\n                h: 0,\n                // radius for rounded corners, default is 0,\n                r: 0,\n                o: 0\n            };\n            try {\n                var width = this.settings.canvas.width;\n                var height = this.settings.canvas.height;\n                if (data.hasOwnProperty('shape')) {\n                    rectParameter.w = data.shape.rx * width * 2;\n                    rectParameter.h = data.shape.ry * height * 2;\n                    rectParameter.x = (data.shape.c.x * width) - rectParameter.w / 2;\n                    rectParameter.y = (data.shape.c.y * height) - rectParameter.h / 2;\n                    rectParameter.o = data.shape.o;\n                    return rectParameter;\n                }\n            }\n            catch (e) {\n                if (this.logger !== null) {\n                    this.logger.error(\"Error to parse data GetRectData \");\n                    this.logger.error(data);\n                }\n            }\n            return null;\n        }\n    });\n"
  },
  {
    "path": "src/plugins/overlay/overlay.js",
    "content": "/**\r\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\r\n *\r\n * This file is part of amalia.js\r\n *\r\n * Amalia.js is free software: you can redistribute it and/or modify it under\r\n * the terms of the MIT License\r\n *\r\n * Redistributions of source code, javascript and css minified versions must\r\n * retain the above copyright notice, this list of conditions and the following\r\n * disclaimer\r\n *\r\n * Neither the name of the copyright holder nor the names of its contributors\r\n * may be used to endorse or promote products derived from this software without\r\n * specific prior written permission\r\n *\r\n * You should have received a copy of the MIT License along with\r\n * amalia.js. If not, see <https://opensource.org/license/mit/>\r\n *\r\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\r\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r\n * A PARTICULAR PURPOSE.\r\n */\r\n/**\r\n * In charge of the overlay plugin\r\n * @class OverlayPlugin\r\n * @namespace fr.ina.amalia.player.plugins.overlay\r\n * @module plugin\r\n * @submodule plugin-overlay\r\n * @constructor\r\n * @extends fr.ina.amalia.player.plugins.PluginBase\r\n * @param {Object} settings\r\n * @param {Object} container\r\n */\r\nfr.ina.amalia.player.plugins.PluginBaseMultiBlocks.extend(\"fr.ina.amalia.player.plugins.OverlayPlugin\", {\r\n        classCss: \"ajs-plugin plugin-overlay\",\r\n        eventTypes: {\r\n            CLICK: \"fr.ina.amalia.player.plugins.OverlayPlugin.eventTypes.CLICK\"\r\n        }\r\n    },\r\n    {\r\n        /**\r\n         * Instance of spatial data parser\r\n         * @property spatialsDataParser\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        spatialsDataParser: null,\r\n        /**\r\n         * Parsed spatial data\r\n         * @property spatialsData\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        spatialsData: null,\r\n        /**\r\n         * Parsed spatial data\r\n         * @property seekSpatialsData\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        seekSpatialsData: null,\r\n        /**\r\n         * Main container\r\n         * @property container\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        container: null,\r\n        /**\r\n         * Instance of raphelsjs\r\n         * @property canvas\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        canvas: null,\r\n        /**\r\n         * Selected metadata id provide by player core.\r\n         * @property loadDataStarted\r\n         * @default ''\r\n         */\r\n        selectedMetadataId: '',\r\n        /**\r\n         * Instance of metadata manager\r\n         * @property metadataManager\r\n         * @default null\r\n         */\r\n        metadataManager: null,\r\n        /**\r\n         * erase state\r\n         * @property eraseState\r\n         * @type {Boolean}\r\n         * @default null\r\n         */\r\n        eraseState: false,\r\n        /**\r\n         * Last selected form\r\n         * @property selectedShape\r\n         * @type {Boolean}\r\n         * @default null\r\n         */\r\n        selectedShape: null,\r\n        /**\r\n         * True when do draw\r\n         * @property doDraw\r\n         * @type {Object}\r\n         * @default false\r\n         */\r\n        doDraw: false,\r\n        /**\r\n         * True when drawing\r\n         * @property drawing\r\n         * @type {Object}\r\n         * @default false\r\n         */\r\n        drawing: false,\r\n        /**\r\n         * drawingHandler\r\n         * @property drawingHandler\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        drawingHandler: null,\r\n        /**\r\n         * Localisation manager\r\n         * @property localisationManager\r\n         * @type {Object}\r\n         * @default null\r\n         */\r\n        localisationManager: null,\r\n        /**\r\n         * Initialize plugin and create container for this plugin\r\n         * @method initializeOnLoadStart\r\n         */\r\n        initialize: function () {\r\n            this.listOfMetadataTypes = [];\r\n            this.notManagedMetadataIds = [];\r\n            this.managedMetadataIds = [];\r\n            this.doDraw = false;\r\n            this.drawing = false;\r\n            this.settings = $.extend({\r\n                    offsetTime: 0.5,\r\n                    metadataId: '',\r\n                    editable: false,\r\n                    defaultSelectedShape: 'rectangle',\r\n                    lineDisplayMode: fr.ina.amalia.player.plugins.PluginBaseMultiBlocks.METADATA_DISPLAY_TYPE.STATIC,\r\n                    callbacks: {}\r\n                },\r\n                this.settings.parameters || {});\r\n            this.container = $('<div>', {\r\n                'class': this.Class.classCss\r\n            });\r\n            this.pluginContainer.append(this.container);\r\n            //Set default shape\r\n            this.setSelectedShape(this.settings.defaultSelectedShape);\r\n            this.canvas = null;\r\n            // initialisation\r\n            this.spatialsData = null;\r\n            this.spatialsDataParser = new fr.ina.amalia.player.plugins.overlay.SpatialsDataParser(this.settings);\r\n            this.localisationManager = new fr.ina.amalia.player.LocalisationManager();\r\n            this.definePlayerListeners();\r\n        },\r\n        /**\r\n         * Fired on load start event\r\n         * @method initializeOnLoadStart\r\n         */\r\n        initializeOnLoadStart: function () {\r\n            this.updateBlockData();\r\n            this.pluginContainer.append(this.container);\r\n            this.createCanvas();\r\n            this.createContextMenuOption();\r\n            this.metadataManager = this.mediaPlayer.getMetadataManager();\r\n            if (this.settings.editable === true) {\r\n                this.createToolBoxCtrl();\r\n                this.createErrorContainer();\r\n                this.createDoDrawRect();\r\n                this.container.on('mousedown', {\r\n                    self: this\r\n                }, this.onMouseDown);\r\n                this.container.on('mouseup', {\r\n                    self: this\r\n                }, this.onMouseUp);\r\n                this.container.on('mousemove', {\r\n                    self: this\r\n                }, this.onMouseMove);\r\n            }\r\n        },\r\n        /**\r\n         * Set player events listener on the player\r\n         * @method defineListeners\r\n         */\r\n        definePlayerListeners: function () {\r\n            var mainContainer = this.mediaPlayer.getContainer();\r\n            // Player events\r\n            mainContainer.one(fr.ina.amalia.player.PlayerEventType.PLUGIN_READY, {\r\n                self: this\r\n            }, this.onFirstTimechange);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.ENDED, {\r\n                self: this\r\n            }, this.onEnd);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\r\n                self: this\r\n            }, this.onTimeupdate);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SEEK, {\r\n                self: this\r\n            }, this.onSeek);\r\n            // call function 200 ms after resize is complete\r\n            $(window).on('debouncedresize', {\r\n                self: this\r\n            }, this.onWindowResize);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\r\n                self: this\r\n            }, this.onSelectedMetadataChange);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\r\n                self: this\r\n            }, this.onDataChange);\r\n            ///bind metadata\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.BIND_METADATA, {\r\n                self: this\r\n            }, this.onBindMetadata);\r\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.UNBIND_METADATA, {\r\n                self: this\r\n            }, this.onUnBindMetadata);\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\r\n            }\r\n        },\r\n        /**\r\n         * Create context menu option\r\n         * @method createContextMenuOption\r\n         */\r\n        createContextMenuOption: function () {\r\n            var item = this.mediaPlayer.addMenuItemWithLink(fr.ina.amalia.player.PlayerMessage.PLUGIN_OVERLAY_CONTEXT_MENU_ENABLED_DISABLED_PLUGIN, \"#\", \"presentation\");\r\n            if (typeof item === \"object\") {\r\n                $(item).on('click', {\r\n                        self: this\r\n                    },\r\n                    function (e) {\r\n                        e.preventDefault();\r\n                        e.data.self.changeDisplayState();\r\n                    });\r\n            }\r\n        },\r\n        /**\r\n         * Return true if is valid type\r\n         * @returns {Boolean}\r\n         */\r\n        isValidMetadataType: function () {\r\n            var metadataType = this.getMetadataType();\r\n            return (metadataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING || metadataType === fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION);\r\n        },\r\n        /**\r\n         * In charge to toggle display container\r\n         * @method changeDisplayState\r\n         */\r\n        changeDisplayState: function () {\r\n            if (this.container.is(':visible')) {\r\n                this.container.hide();\r\n            }\r\n            else {\r\n                this.container.show();\r\n            }\r\n        },\r\n        /**\r\n         * In charge to update block data\r\n         */\r\n        updateBlockData: function () {\r\n            // use settings metadata if not empty\r\n            if (this.settings.metadataId !== '') {\r\n                this.spatialsData = this.updateMetadata(this.settings.metadataId, 0, this.mediaPlayer.getDuration());\r\n            }\r\n            else {\r\n                var listOfIds = this.getBindIds();\r\n                this.spatialsData = [];\r\n                for (var i = 0;\r\n                     i < listOfIds.length;\r\n                     i++) {\r\n                    var metadataObject = this.mediaPlayer.getBlockMetadata(listOfIds[i]);\r\n                    var color = \"\";\r\n                    if (metadataObject !== null && metadataObject.hasOwnProperty('viewControl') && metadataObject.viewControl !== null) {\r\n                        color = metadataObject.viewControl.hasOwnProperty('color') ? metadataObject.viewControl.color : '';\r\n                    }\r\n                    var spatialsData = this.updateMetadata(listOfIds[i], 0, this.mediaPlayer.getDuration(), color);\r\n\r\n                    this.spatialsData = this.spatialsData.concat(spatialsData);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * In charge to get metadata by id,tcin and tcout\r\n         * @param {String} metadataId\r\n         * @param {Number} tcin\r\n         * @param {Number} tcout\r\n         */\r\n        updateMetadata: function (metadataId, tcin, tcout, color) {\r\n            var data = this.mediaPlayer.getMetadataWithRange(metadataId, tcin, tcout);\r\n            return this.spatialsDataParser.parserSpacialMetadata(data, metadataId, color);\r\n        },\r\n        /**\r\n         * In charge to update current time position\r\n         * @method updatePos\r\n         * @param {Number} currentTime\r\n         */\r\n        updatePos: function (currentTime) {\r\n            if (typeof this.spatialsData !== \"undefined\" && this.spatialsData !== null && typeof this.spatialsData === \"object\" && this.spatialsData.length > 0) {\r\n                currentTime = parseFloat(currentTime);\r\n                // if paused get calculating seek position\r\n                var isPaused = (this.mediaPlayer.isPaused());\r\n                var displayData = isPaused ? this.getSeekDisplayItems(currentTime) : this.getDisplayItems(currentTime);\r\n                if (displayData.length > 0) {\r\n                    this.createObject(displayData, isPaused);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Return display items\r\n         * @method getDisplayItems\r\n         * @param {Number} currentTime\r\n         * @return {Array}\r\n         */\r\n        getDisplayItems: function (currentTime) {\r\n            var item = null;\r\n            var displayData = [];\r\n            for (var i = 0;\r\n                 i < this.spatialsData.length;\r\n                 i++) {\r\n                item = this.spatialsData[i];\r\n                if (typeof item === \"object\" && item.hasOwnProperty('tcin') && currentTime >= parseFloat(item.tcin) && currentTime <= parseFloat(item.tcin) + this.settings.offsetTime) {\r\n                    displayData.push(item);\r\n                    this.spatialsData.splice(i, 1);\r\n                }\r\n            }\r\n            return displayData;\r\n        },\r\n        /**\r\n         * In charge to create canvas\r\n         * @method createCanvas\r\n         */\r\n        createCanvas: function () {\r\n            var videoSize = this.getVideoSize();\r\n            var width = videoSize.w;\r\n            var height = videoSize.h;\r\n            this.canvas = new Raphael(this.container.get(0), width, height);\r\n            this.canvas.canvas.className.baseVal = \"overlay-canvas\";\r\n            this.updateCanvasPosition();\r\n            // Add event\r\n            this.container.on(fr.ina.amalia.player.plugins.overlay.DrawBase.eventTypes.CLICK, {\r\n                    self: this\r\n                },\r\n                this.onClickAtObject);\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"CreateCanvas width:\" + width + \" height: \" + height);\r\n            }\r\n        },\r\n        /**\r\n         * Return Video size\r\n         * @method getVideoSize\r\n         */\r\n        getVideoSize: function () {\r\n            var player = this.mediaPlayer.getMediaPlayer();\r\n            var videoHeight = player.get(0).videoHeight;\r\n            var videoWidth = player.get(0).videoWidth;\r\n            var widthRatio = player.width() / videoWidth;\r\n            var heightRatio = player.height() / videoHeight;\r\n            var ratio = Math.min(widthRatio, heightRatio);\r\n            return {\r\n                w: ratio * videoWidth,\r\n                h: ratio * videoHeight\r\n            };\r\n        },\r\n        /**\r\n         * In charge to update canvas position\r\n         * @method updateCanvasPosition\r\n         */\r\n        updateCanvasPosition: function () {\r\n            var videoSize = this.getVideoSize();\r\n            var player = this.mediaPlayer.getMediaPlayer();\r\n            this.container.find('.overlay-canvas').css({\r\n                'left': (player.width() - videoSize.w) / 2,\r\n                'top': (player.height() - videoSize.h) / 2\r\n            });\r\n        },\r\n        /**\r\n         * In charge to create canvas object\r\n         * @method createObject\r\n         * @param {Object} data\r\n         */\r\n        createObject: function (data) {\r\n            if (data !== null && typeof data !== \"undefined\") {\r\n                var settings = $.extend({\r\n                    canvas: this.canvas,\r\n                    container: this.container,\r\n                    debug: this.settings.debug,\r\n                    color: ''\r\n                }, this.settings || {});\r\n                var track = null;\r\n                var object = null;\r\n                if (typeof data === \"object\" && data.length > 0) {\r\n                    for (var i = 0;\r\n                         i < data.length;\r\n                         ++i) {\r\n                        track = data[i];\r\n                        settings.editable = (this.settings.editable === true && track.metadataId === this.getSelectedMetadataId());\r\n                        switch (track.hasOwnProperty('type') ? track.type : '') {\r\n                            case 'rectangle':\r\n                                object = new fr.ina.amalia.player.plugins.overlay.DrawRect(settings, this.mediaPlayer, data[i], this);\r\n                                break;\r\n                            case 'ellipse':\r\n                                object = new fr.ina.amalia.player.plugins.overlay.DrawEllipse(settings, this.mediaPlayer, data[i], this);\r\n                                break;\r\n                            default :\r\n                                if (this.logger !== null) {\r\n                                    this.logger.warn(this.Class.fullName + \": L'objet ne peut pas être interpreté.\");\r\n                                }\r\n                                break;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            else {\r\n                this.logger.error(\"Error to create object\");\r\n                this.logger.error(data);\r\n            }\r\n            return null;\r\n        },\r\n        /**\r\n         * In charge to clear canvas\r\n         * @method clearCanvas\r\n         */\r\n        clearCanvas: function () {\r\n            this.canvas.remove();\r\n            this.createCanvas();\r\n            this.spatialsData = this.spatialsDataParser.getData();\r\n            this.createDoDrawRect();\r\n            if (this.logger !== null) {\r\n                this.logger.trace(this.Class.fullName, \"clearCanvas : \" + this.spatialsData.length);\r\n            }\r\n        },\r\n        /**\r\n         * In charge to create toolbox element\r\n         */\r\n        createToolBoxCtrl: function () {\r\n            var _toolboxElement = $('<div>', {\r\n                'class': 'toolbox'\r\n            }).draggable({\r\n                containment: \"parent\",\r\n                scroll: false\r\n            });\r\n            _toolboxElement.css('position', 'absolute');\r\n            _toolboxElement.append(this.createAddToolboxItem());\r\n            _toolboxElement.append(this.createEraserToolboxItem());\r\n            _toolboxElement.append(this.createNavCtrlToolboxItem());\r\n            //Add events display logic\r\n            _toolboxElement.find('.add-shapebox .add div.add-ctrl span.ctrl').on('click', {\r\n                self: this\r\n            }, function (event) {\r\n                event.data.self.openAddShape();\r\n            });\r\n            _toolboxElement.find('.add-shapebox .add div.add-selectFormBox div.close-box span.ctrl').on('click', {\r\n                self: this\r\n            }, function (event) {\r\n                event.data.self.closeAddShape();\r\n            });\r\n            _toolboxElement.find('.erase-box span.ctrl').on('click', {\r\n                self: this\r\n            }, this.onClickToEraser);\r\n            this.container.append(_toolboxElement);\r\n        },\r\n        /**\r\n         * In charge to create add button and select form items\r\n         * @method createAddToolboxItem\r\n         */\r\n        createAddToolboxItem: function () {\r\n            var _addShapeBox = $('<div>', {\r\n                'class': 'add-shapebox'\r\n            });\r\n            var _addBox = $('<div>', {\r\n                'class': 'add'\r\n            });\r\n            //Add Ctrl\r\n            var _addCtrlBox = $('<div>', {\r\n                'class': 'add-ctrl'\r\n            });\r\n            var _addCtrl = $('<span>', {\r\n                'class': 'ctrl ajs-icon ajs-icon-plus'\r\n            });\r\n            _addCtrlBox.append(_addCtrl);\r\n            _addBox.append(_addCtrlBox);\r\n            // select form\r\n            var _selectFormBox = $('<div>', {\r\n                'class': 'add-selectFormBox'\r\n            }).hide();\r\n            var _closeCtrlBox = $('<div>', {\r\n                'class': 'close-box'\r\n            });\r\n            var _closeCtrl = $('<span>', {\r\n                'class': 'ctrl ajs-icon ajs-icon-remove'\r\n            });\r\n            _closeCtrlBox.append(_closeCtrl);\r\n            _selectFormBox.append(_closeCtrlBox);\r\n            var _addShapeMsgBox = $('<div>', {\r\n                'class': 'add-shape-msg',\r\n                'text': 'Sélectionnez une forme'\r\n            });\r\n            _selectFormBox.append(_addShapeMsgBox);\r\n            //Rect\r\n            var _addShapeRectMsgBox = $('<div>', {\r\n                'class': 'add-shape-rectangle'\r\n            });\r\n            var _rectIcon = $('<span>', {\r\n                'class': 'ajs-icon ajs-icon-stop'\r\n            }).on('click', {\r\n                self: this\r\n            }, function (event) {\r\n                event.data.self.setSelectedShape('rectangle');\r\n            });\r\n            _addShapeRectMsgBox.append(_rectIcon);\r\n            _selectFormBox.append(_addShapeRectMsgBox);\r\n            _addBox.append(_selectFormBox);\r\n            _addShapeBox.append(_addBox);\r\n            return _addShapeBox;\r\n        },\r\n        /**\r\n         * In charge to create eraser button\r\n         * @method createAddToolboxItem\r\n         */\r\n        createEraserToolboxItem: function () {\r\n            //Erase Ctrl\r\n            var _eraseCtrlBox = $('<div>', {\r\n                'class': 'erase-box'\r\n            });\r\n            var _eraseCtrl = $('<span>', {\r\n                'class': 'ctrl ajs-icon ajs-icon-eraser'\r\n            });\r\n            _eraseCtrlBox.append(_eraseCtrl);\r\n            return _eraseCtrlBox;\r\n        },\r\n        /**\r\n         * In charge to create nav button\r\n         * @method createAddToolboxItem\r\n         */\r\n        createNavCtrlToolboxItem: function () {\r\n            //Nav Ctrl\r\n            var _navBox = $('<div>', {\r\n                'class': 'nav-box'\r\n            });\r\n            //Left Ctrl\r\n            var _leftCtrlBox = $('<div>', {\r\n                'class': 'nav-left-box'\r\n            });\r\n            var _leftCtrl = $('<span>', {\r\n                'class': 'ctrl ajs-icon ajs-icon-chevron-left'\r\n            });\r\n            _leftCtrlBox.append(_leftCtrl);\r\n            _navBox.append(_leftCtrlBox);\r\n            //Right Ctrl\r\n            var _rightCtrlBox = $('<div>', {\r\n                'class': 'nav-right-box'\r\n            });\r\n            var _rightCtrl = $('<span>', {\r\n                'class': 'ctrl ajs-icon ajs-icon-chevron-right'\r\n            });\r\n            _rightCtrlBox.append(_rightCtrl);\r\n            _navBox.append(_rightCtrlBox).hide();\r\n            return _navBox;\r\n        },\r\n        /**\r\n         * In charge to create error container\r\n         * @returns {undefined}\r\n         */\r\n        createErrorContainer: function () {\r\n            var errorContainer = $('<div>', {\r\n                'class': 'error'\r\n            }).hide();\r\n            this.container.append(errorContainer);\r\n        },\r\n        /**\r\n         * In charge to set error message\r\n         * @param {type} msg\r\n         * @returns {plugin-overlay-editorAnonym$1.setErrorMsg.plugin-overlay-editorAnonym$9}\r\n         */\r\n        setErrorMsg: function (msg) {\r\n            var message = $('<span>', {\r\n                'class': 'msg',\r\n                'text': msg\r\n            });\r\n            this.container.find('.error').empty().append(message).show();\r\n        },\r\n        /**\r\n         * In charge to clear all error messages\r\n         * @returns {undefined}\r\n         */\r\n        clearErrorMsg: function () {\r\n            this.container.find('.error').empty().hide();\r\n        },\r\n        /**\r\n         * In charge to open add shape block and select last used shape\r\n         */\r\n        openAddShape: function () {\r\n            if (this.startDraw()) {\r\n                var _toolboxElement = this.container.find('.toolbox');\r\n                _toolboxElement.find('.add .add-ctrl').hide();\r\n                _toolboxElement.find('.add .add-selectFormBox').show();\r\n                _toolboxElement.find('.add-selectFormBox div').removeClass('on');\r\n                _toolboxElement.find('.add-selectFormBox div.add-shape-' + this.selectedShape).addClass('on');\r\n                //erase\r\n                _toolboxElement.find('.erase-box').hide();\r\n            }\r\n        },\r\n        /**\r\n         * In charge to open add shape block and select last used shape\r\n         */\r\n        closeAddShape: function () {\r\n            var _toolboxElement = this.container.find('.toolbox');\r\n            _toolboxElement.find('.add .add-selectFormBox').hide();\r\n            _toolboxElement.find('.add div.add-ctrl').show();\r\n            _toolboxElement.find('.erase-box').show();\r\n            this.endDraw();\r\n        },\r\n        /**\r\n         * return  eraseState\r\n         */\r\n        getEraseState: function () {\r\n            return this.eraseState;\r\n        },\r\n        /**\r\n         * set erase state\r\n         * @param {type} val\r\n         */\r\n        setEraseState: function (val) {\r\n            this.eraseState = val;\r\n            this.settings.eraseState = val;\r\n            this.container.toggleClass('eraser', val);\r\n            this.container.find('.toolbox').find('.erase-box').toggleClass('on', val);\r\n        },\r\n        /**\r\n         * Set shape\r\n         * @param {String} shape\r\n         */\r\n        setSelectedShape: function (shape) {\r\n            if ($.inArray(shape, this.listOfShapes) > -1) {\r\n                this.selectedShape = shape;\r\n            }\r\n            else {\r\n                this.selectedShape = this.settings.defaultSelectedShape;\r\n            }\r\n            var _toolboxElement = this.container.find('.toolbox');\r\n            _toolboxElement.find('.add-selectFormBox div').removeClass('on');\r\n            _toolboxElement.find('.add-selectFormBox div.add-shape-' + this.selectedShape).addClass('on');\r\n        },\r\n        /**\r\n         * Return selected shape\r\n         * @returns {plugin-overlay-editorAnonym$1.settings.selectedShape|String}\r\n         */\r\n        getSelectedShape: function () {\r\n            return this.selectedShape;\r\n        },\r\n        /**\r\n         * Star draw object\r\n         * @returns {undefined}\r\n         */\r\n        startDraw: function () {\r\n            if (this.getSelectedMetadataId() !== null) {\r\n                if (this.isValidMetadataType()) {\r\n                    this.doDraw = true;\r\n                    this.container.addClass('draw');\r\n                    this.setEraseState(false);\r\n                    this.mediaPlayer.pause();\r\n                    return true;\r\n                }\r\n                else {\r\n                    this.setErrorMsg(fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA_ITEM_TYPE);\r\n                }\r\n            }\r\n            else {\r\n                this.setErrorMsg(fr.ina.amalia.player.PlayerMessage.PLUGIN_METADATA_ITEMS_EDITOR_NEED_METADTA);\r\n            }\r\n            return false;\r\n        },\r\n        /**\r\n         * Return metadata bock type\r\n         * @returns {undefined}\r\n         */\r\n        getMetadataType: function () {\r\n            var metadataBlock = this.metadataManager.getBlockMetadata(this.getSelectedMetadataId());\r\n            if (metadataBlock !== null && metadataBlock.type !== \"\") {\r\n                return metadataBlock.type;\r\n            }\r\n            return null;\r\n        },\r\n        /**\r\n         * Return selected metadata id\r\n         * @method getSelectedMetadataId\r\n         */\r\n        getSelectedMetadataId: function () {\r\n            return this.selectedMetadataId;\r\n        },\r\n        /**\r\n         * Return selected metadata id\r\n         * @method setSelectedMetadataId\r\n         * @param metadataId\r\n         */\r\n        setSelectedMetadataId: function (metadataId) {\r\n            if (typeof metadataId === \"string\") {\r\n                this.selectedMetadataId = metadataId;\r\n                this.clearErrorMsg();\r\n            }\r\n            else {\r\n                this.selectedMetadataId = null;\r\n            }\r\n        },\r\n        /**\r\n         * End draw object\r\n         * @returns {undefined}\r\n         */\r\n        endDraw: function () {\r\n            this.doDraw = false;\r\n            this.container.removeClass('draw');\r\n            this.container.find('.toolbox .add-ctrl').removeClass('on');\r\n            this.setEraseState(false);\r\n        },\r\n        /**\r\n         * In charge to draw rect\r\n         */\r\n        createDoDrawRect: function () {\r\n            this.drawingHandler = this.canvas.rect(0, 0, 10, 10);\r\n            this.drawingHandler.fig = \"DoDrawRect\";\r\n            this.drawingHandler.attr({\r\n                // Attributes of the element\r\n                //\"fill\" : \"#3cf\",\r\n                \"stroke\": \"#3cf\"\r\n            });\r\n            this.drawingHandler.toFront();\r\n            this.drawingHandler.hide();\r\n            this.drawingHandler.doDraw = {};\r\n        },\r\n        /**\r\n         * In charge to draw shape\r\n         * @param shapeType {String} shapeType\r\n         * @param x {Number} x coordinate of specified position\r\n         * @param y {Number} y coordinate of specified position\r\n         * @param w {Number} w width\r\n         * @param h {Number} h height\r\n         */\r\n        drawShape: function (shapeType, x, y, w, h) {\r\n            var shape = null;\r\n            if (shapeType === \"ellipse\") {\r\n                shape = this.canvas.ellipse(x, y, w, h);\r\n                shape.fig = \"ellipse\";\r\n            }\r\n            else {\r\n                // default shape rectangle\r\n                shape = this.canvas.rect(x, y, w, h);\r\n                shape.fig = \"rectangle\";\r\n            }\r\n\r\n            if (shape) {\r\n                shape.objectId = 'spatial_' + fr.ina.amalia.player.helpers.UtilitiesHelper.generateUUID();\r\n                shape.attr({\r\n                    // Attributes of the element\r\n                    //\"fill\" : \"#dfed48\",\r\n                    \"stroke\": \"#3cf\"\r\n                });\r\n                // Initial values of applied transforms\r\n                shape.translate = [\r\n                    0,\r\n                    0\r\n                ];\r\n                shape.scale = [\r\n                    1,\r\n                    1\r\n                ];\r\n                shape.rotate = 0;\r\n                shape.data.self = this;\r\n                var transformConfig = {\r\n                    keepRatio: false,\r\n                    fill: 'black',\r\n                    draw: [\r\n                        'bbox',\r\n                        'circle'\r\n                    ],\r\n                    range: {\r\n                        scale: [\r\n                            0,\r\n                            99999\r\n                        ]\r\n                    }\r\n                };\r\n                // Add freeTransform\r\n                var _ft = this.canvas.freeTransform(shape, transformConfig, this.onTransformationCallback);\r\n                _ft.self = this;\r\n                return shape;\r\n            }\r\n            return null;\r\n        },\r\n        /**\r\n         * In charge to create data for shape\r\n         * @param {Object} shape\r\n         * @param {Object} shapePos\r\n         */\r\n        createDataShape: function (shape, shapePos) {\r\n            var _player = this.mediaPlayer;\r\n            var _data = {\r\n                shape: shapePos,\r\n                sublocalisations: null,\r\n                tclevel: 0,\r\n                thumb: null,\r\n                type: null,\r\n                tc: _player.getCurrentTime(),\r\n                tcin: _player.getCurrentTime(),\r\n                label: shape.hasOwnProperty('objectId') ? shape.objectId : ''\r\n            };\r\n            shape.data = _data;\r\n            //Ref loc\r\n            _data.refLoc = _player.addMetadataItem(this.getSelectedMetadataId(), _data);\r\n            return _data;\r\n        },\r\n        /**\r\n         * In charge to refrech spatial data\r\n         * @returns {undefined}\r\n         */\r\n        refreshSeekData: function () {\r\n            // use settings metadata if not empty\r\n            if (this.settings.metadataId !== '') {\r\n                this.seekSpatialsData = this.updateMetadata(this.settings.metadataId, 0, this.mediaPlayer.getDuration());\r\n            }\r\n            else {\r\n                var listOfIds = this.getBindIds();\r\n                this.seekSpatialsData = [];\r\n                for (var i = 0;\r\n                     i < listOfIds.length;\r\n                     i++) {\r\n                    var seekSpatialsData = this.updateMetadata(listOfIds[i], 0, this.mediaPlayer.getDuration());\r\n                    this.seekSpatialsData = this.seekSpatialsData.concat(seekSpatialsData);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Return display items\r\n         * @method getDisplayItems\r\n         * @param {Number} currentTime\r\n         * @return {Array}\r\n         */\r\n        getSeekDisplayItems: function (currentTime) {\r\n            var item = null;\r\n            var displayData = [];\r\n            if (this.seekSpatialsData !== null) {\r\n                for (var i = 0;\r\n                     i < this.seekSpatialsData.length;\r\n                     i++) {\r\n                    item = this.seekSpatialsData[i];\r\n                    if (typeof item === \"object\" && item.hasOwnProperty('tcin') && item.hasOwnProperty('tcout') && currentTime >= parseFloat(item.tcin) && currentTime <= parseFloat(item.tcout)) {\r\n                        //reset by default value\r\n                        item.start = jQuery.extend(true, {}, this.seekSpatialsData[i].start);\r\n                        var seekPos = this.spatialInterpolation(item.start, item.end, this.mediaPlayer.getCurrentTime());\r\n                        item.start.shape.c.x = seekPos.x;\r\n                        item.start.shape.c.y = seekPos.y;\r\n                        item.start.shape.rx = seekPos.rx;\r\n                        item.start.shape.ry = seekPos.ry;\r\n\r\n                        displayData.push(item);\r\n                        this.seekSpatialsData.splice(i, 1);\r\n                    }\r\n                }\r\n            }\r\n            return displayData;\r\n        },\r\n        /**\r\n         * return position\r\n         * @param {Number} ptcin\r\n         * @param {Number} ptcout\r\n         * @param {Number} tcin\r\n         * @param {Number} tcout\r\n         * @param {Number} tc\r\n         * @returns {Number} position\r\n         */\r\n        linearInterpolation: function (ptcin, ptcout, tcin, tcout, tc) {\r\n            return ptcin + ((ptcout - ptcin) / (tcout - tcin)) * (tc - tcin);\r\n        },\r\n        /**\r\n         * Return sptial position\r\n         * @param {Object} spi\r\n         * @param {Object} spo\r\n         * @param {Object} tc\r\n         * param {String} type\r\n         */\r\n        spatialInterpolation: function (spi, spo, tc) {\r\n            var _x = this.linearInterpolation(spi.shape.c.x, spo.shape.c.x, spi.tc, spo.tc, tc);\r\n            var _y = this.linearInterpolation(spi.shape.c.y, spo.shape.c.y, spi.tc, spo.tc, tc);\r\n            var _rx = this.linearInterpolation(spi.shape.rx, spo.shape.rx, spi.tc, spo.tc, tc);\r\n            var _ry = this.linearInterpolation(spi.shape.ry, spo.shape.ry, spi.tc, spo.tc, tc);\r\n            return {\r\n                x: _x,\r\n                y: _y,\r\n                rx: _rx,\r\n                ry: _ry\r\n            };\r\n        },\r\n        // /**Player events**/\r\n        /**\r\n         * Fired when windows resize event and call clear the canvas\r\n         * @method onWindowResize\r\n         * @param {Object} event\r\n         */\r\n        onWindowResize: function (event) {\r\n            var videoSize = event.data.self.getVideoSize();\r\n            var width = videoSize.w;\r\n            var height = videoSize.h;\r\n            if (this.canvas !== null) {\r\n                event.data.self.clearCanvas();\r\n                event.data.self.canvas.setSize(width, height);\r\n                event.data.self.updateCanvasPosition();\r\n            }\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onWindowResize W: \" + width + \" H:\" + height);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on first time change event\r\n         * @method onFirstTimechange\r\n         * @param {Object} event\r\n         */\r\n        onFirstTimechange: function (event) {\r\n            event.data.self.initializeOnLoadStart();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"timechange\");\r\n            }\r\n        },\r\n        /**\r\n         * Fired on end event\r\n         * @method onEnd\r\n         * @param {Object} event\r\n         */\r\n        onEnd: function (event) {\r\n            event.data.self.clearCanvas();\r\n        },\r\n        /**\r\n         * Fired on time change event to clear canvas\r\n         * @method onTimeupdate\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onTimeupdate: function (event, data) {\r\n            event.data.self.updatePos(parseFloat(data.currentTime));\r\n        },\r\n        /**\r\n         * Fired on seek event to clear canvas\r\n         * @method onSeek\r\n         * @param {Object} event\r\n         */\r\n        onSeek: function (event) {\r\n            event.data.self.clearCanvas();\r\n            event.data.self.updateBlockData();\r\n            event.data.self.refreshSeekData();\r\n        },\r\n        /**\r\n         * Fired on click event\r\n         * @method onClickAtObject\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onClickAtObject: function (event, data) {\r\n            if (typeof event.data.self.settings.parameters !== \"undefined\") {\r\n                try {\r\n                    /* jslint evil: true */\r\n                    eval(event.data.self.settings.parameters.callbacks.click + '(data)');\r\n                }\r\n                catch (e) {\r\n                    if (event.data.self.logger !== null) {\r\n                        event.data.self.logger.warn(\"Send callback failed.\");\r\n                    }\r\n                }\r\n            }\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.info(event.data.self.Class.fullName, \"onClickAtObject\");\r\n                event.data.self.logger.warn(event.data);\r\n                event.data.self.logger.warn(data);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on selected metadata change\r\n         * @method onSelectedMetadataChange\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onSelectedMetadataChange: function (event, data) {\r\n            if (data.metadataId !== null) {\r\n                event.data.self.setSelectedMetadataId(data.metadataId);\r\n            }\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectedMetadataChange id:\" + data.metadataId);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on data change event\r\n         * @param {type} event\r\n         * @param {type} data\r\n         */\r\n        onDataChange: function (event, data) {\r\n            if (event.data.self.loadDataStarted === false) {\r\n                if (event.data.self.isManagedMetadataId(data.id)) {\r\n                    if (event.data.self.isBoundMetadataId(data.id)) {\r\n                        event.data.self.updateBlockData();\r\n                    }\r\n                }\r\n                else {\r\n                    event.data.self.bindMetadataId(data.id);\r\n                }\r\n            }\r\n            else if (event.data.self.dataToDeal !== null) {\r\n                // Add to deal array if display mode is dynamic\r\n                if (event.data.self.settings.lineDisplayMode > 1) {\r\n                    event.data.self.dataToDeal.push(data.id);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Fired on begin data change event\r\n         * @method onBeginDataChange\r\n         * @param {Object} event\r\n         */\r\n        onBeginDataChange: function (event) {\r\n            event.data.self.loadDataStarted = true;\r\n            for (var i = 0;\r\n                 i < event.data.self.dataToDeal.length;\r\n                 i++) {\r\n                event.data.self.bindMetadataId(event.data.self.dataToDeal[i]);\r\n            }\r\n            event.data.self.dataToDeal = [];\r\n            event.data.self.updateBlockData();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onBeginDataChange\");\r\n            }\r\n        },\r\n        /**\r\n         * Fired on end data change event\r\n         * @method onEndDataChange\r\n         * @param {Object} event\r\n         */\r\n        onEndDataChange: function (event) {\r\n            event.data.self.loadDataStarted = false;\r\n            event.data.self.updateBlockData();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onEndDataChange\");\r\n            }\r\n        },\r\n        /**\r\n         * Fired on bind metadata event\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onBindMetadata: function (event, data) {\r\n            event.data.self.bindMetadataId(data.id);\r\n            event.data.self.updateBlockData();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onBindMetadata Id:\" + data.id);\r\n            }\r\n        },\r\n        /**\r\n         * Fired on unbind metadata event\r\n         * @param {Object} event\r\n         * @param {Object} data\r\n         */\r\n        onUnBindMetadata: function (event, data) {\r\n            event.data.self.unbindMetadataId(data.id);\r\n            event.data.self.updateBlockData();\r\n            if (event.data.self.logger !== null) {\r\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onUnBindMetadata Id: \" + data.id);\r\n            }\r\n        },\r\n        /**\r\n         * Call when mouse down\r\n         * @method onMouseDown\r\n         * @param {Object} event\r\n         */\r\n        onMouseDown: function (event) {\r\n            if ((event.data.self.doDraw === true && event.ctrlKey === true && event.data.self.drawing === false) || (event.shiftKey === true && event.ctrlKey === true)) {\r\n                var videoSize = event.data.self.getVideoSize();\r\n                var startX = Math.max(0, event.offsetX - ((event.data.self.container.height() - 45 - videoSize.h) / 2));\r\n                var startY = Math.max(0, event.offsetY - ((event.data.self.container.height() - videoSize.h) / 2));\r\n                event.data.self.drawing = true;\r\n                event.data.self.drawingHandler.show();\r\n                event.data.self.drawingHandler.toFront();\r\n                event.data.self.drawingHandler.doDraw.startX = startX;\r\n                event.data.self.drawingHandler.doDraw.startY = startY + 20;\r\n                event.data.self.drawingHandler.attr('x', event.data.self.drawingHandler.doDraw.startX);\r\n                event.data.self.drawingHandler.attr('y', event.data.self.drawingHandler.doDraw.startY);\r\n            }\r\n        },\r\n        /**\r\n         * Call when mouse up\r\n         * @method onMouseUp\r\n         * @param {Object} event\r\n         */\r\n        onMouseUp: function (event) {\r\n            if (event.data.self.drawing === true) {\r\n                event.data.self.drawing = false;\r\n                event.data.self.drawingHandler.hide();\r\n                var w = Math.max(0, event.data.self.drawingHandler.doDraw.endX - event.data.self.drawingHandler.doDraw.startX);\r\n                var h = Math.max(0, event.data.self.drawingHandler.doDraw.endY - event.data.self.drawingHandler.doDraw.startY);\r\n                var _shape = event.data.self.drawShape('rectangle', event.data.self.drawingHandler.doDraw.startX, event.data.self.drawingHandler.doDraw.startY, w, h);\r\n                if (_shape !== null) {\r\n                    var _shapePos = {\r\n                        c: {\r\n                            x: parseFloat((event.data.self.drawingHandler.doDraw.startX + event.data.self.drawingHandler.doDraw.endX) / 2 / event.data.self.canvas.width),\r\n                            y: parseFloat((event.data.self.drawingHandler.doDraw.startY + event.data.self.drawingHandler.doDraw.endY) / 2 / event.data.self.canvas.height)\r\n                        },\r\n                        rx: parseFloat((w / event.data.self.canvas.width) / 2),\r\n                        ry: parseFloat((h / event.data.self.canvas.height) / 2),\r\n                        o: 0,\r\n                        t: event.data.self.getSelectedShape()\r\n                    };\r\n                    event.data.self.createDataShape(_shape, _shapePos);\r\n                }\r\n            }\r\n        },\r\n        /**\r\n         * Call when mouse move\r\n         * @method onMouseMove\r\n         * @param {Object} event\r\n         */\r\n        onMouseMove: function (event) {\r\n            if (event.data.self.drawing === true) {\r\n                var videoSize = event.data.self.getVideoSize();\r\n                var endX = Math.max(0, event.offsetX - ((event.data.self.container.height() - 45 - videoSize.h) / 2));\r\n                var endY = Math.max(0, event.offsetY - ((event.data.self.container.height() - videoSize.h) / 2));\r\n                var width = Math.max(0, endX - event.data.self.drawingHandler.doDraw.startX);\r\n                var height = Math.max(0, endY - event.data.self.drawingHandler.doDraw.startY);\r\n                event.data.self.drawingHandler.doDraw.endX = endX;\r\n                event.data.self.drawingHandler.doDraw.endY = endY;\r\n                event.data.self.drawingHandler.attr('width', width);\r\n                event.data.self.drawingHandler.attr('height', height);\r\n            }\r\n        },\r\n        /**\r\n         * Call when end transformation\r\n         * @param {type} ft\r\n         * @param {type} events\r\n         */\r\n        onTransformationCallback: function (ft, events) {\r\n            if (ft.self.getEraseState() === true) {\r\n                ft.subject.data.delete = true;\r\n                //Clear deleted data\r\n                ft.self.localisationManager.updateSpacialLocBlock(ft.self.mediaPlayer.getMetadataById(ft.self.getSelectedMetadataId()));\r\n                ft.unplug();\r\n                ft.subject.remove();\r\n                ft.self.setEraseState(false);\r\n                ft.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\r\n                    id: ft.self.getSelectedMetadataId()\r\n                });\r\n            }\r\n            else if ($.inArray('drag end', events) > -1 || $.inArray('scale end', events) > -1) {\r\n                //translate pos is a center object\r\n                var _shapePos = {\r\n                    c: {\r\n                        x: parseFloat((ft.attrs.center.x + ft.attrs.translate.x) / ft.self.canvas.width),\r\n                        y: parseFloat((ft.attrs.center.y + ft.attrs.translate.y) / ft.self.canvas.height)\r\n                    },\r\n                    rx: parseFloat((ft.attrs.size.x * ft.attrs.scale.x) / 2 / ft.self.canvas.width),\r\n                    ry: parseFloat((ft.attrs.size.y * ft.attrs.scale.y) / 2 / ft.self.canvas.height),\r\n                    o: parseFloat(ft.attrs.rotate),\r\n                    t: ft.self.shape\r\n                };\r\n\r\n                ft.self.updatePosShape(_shapePos, ft.subject.data);\r\n            }\r\n        },\r\n        /**\r\n         * On eraser\r\n         * @param {Object} event\r\n         */\r\n        onClickToEraser: function (event) {\r\n            if (event.data.self.getEraseState() === true) {\r\n                event.data.self.setEraseState(false);\r\n            }\r\n            else {\r\n                event.data.self.setEraseState(true);\r\n            }\r\n        },\r\n        /**\r\n         * In charge to update shape position\r\n         * @param {Object} shape\r\n         * @param {Object} shapePos\r\n         */\r\n        updatePosShape: function (shapePos, data) {\r\n            var _player = this.mediaPlayer;\r\n            var tc = _player.getCurrentTime();\r\n            if (data.hasOwnProperty('refLoc') && typeof data.refLoc === \"object\" && data.refLoc.tc !== tc) {\r\n                if (data.refLoc.hasOwnProperty('sublocalisations') === false || data.refLoc.sublocalisations === null || typeof data.refLoc.sublocalisations !== \"object\") {\r\n                    //init sublocalisation\r\n                    data.refLoc.sublocalisations = {\r\n                        localisation: []\r\n                    };\r\n                    var oldTc = parseFloat(data.refLoc.tc);\r\n                    var firstShapePos = jQuery.extend({}, {\r\n                        tc: oldTc,\r\n                        shape: data.refLoc.shape,\r\n                        tclevel: 1\r\n\r\n                    });\r\n                    data.refLoc.sublocalisations.localisation.push(firstShapePos);\r\n                    data.refLoc.shape = null;\r\n                    if (oldTc < tc) {\r\n                        data.refLoc.tcin = oldTc;\r\n                        data.refLoc.tc = oldTc;\r\n                        data.refLoc.tcout = tc;\r\n                    }\r\n                    else {\r\n                        data.refLoc.tcin = tc;\r\n                        data.refLoc.tc = tc;\r\n                        data.refLoc.tcout = oldTc;\r\n                    }\r\n                }\r\n                //search if localisations pos for this tc\r\n                var duplicateItem = $.grep(data.refLoc.sublocalisations.localisation, function (a) {\r\n                    return a.tc === tc;\r\n                });\r\n                if (duplicateItem.length > 0) {\r\n                    duplicateItem[0].shape = shapePos;\r\n                }\r\n                else {\r\n                    // add new shap pos\r\n                    data.refLoc.sublocalisations.localisation.push({\r\n                        tc: tc,\r\n                        shape: shapePos,\r\n                        tclevel: 1\r\n                    });\r\n                }\r\n\r\n                _player.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\r\n                    id: data.hasOwnProperty('metadataId') ? data.metadataId : _player.getSelectedMetadataId()\r\n                });\r\n            }\r\n            else if (data.hasOwnProperty('refLoc') && typeof data.refLoc === \"object\") {\r\n                data.refLoc.shape = shapePos;\r\n                _player.getMediaPlayer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\r\n                    id: data.hasOwnProperty('metadataId') ? data.metadataId : _player.getSelectedMetadataId()\r\n                });\r\n            }\r\n        }\r\n    });\r\n"
  },
  {
    "path": "src/plugins/overlay/spatials-data-parser.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Class de parser spatials data and used to draw rectangle and ellipse\n * @class SpatialsDataParser\n * @namespace fr.ina.amalia.player.plugins.overlay\n * @module plugin\n * @submodule plugin-overlay\n * @extends fr.ina.amalia.player.plugins.overlay\n */\n$.Class(\"fr.ina.amalia.player.plugins.overlay.SpatialsDataParser\", {}, {\n    /**\n     * Parsed data\n     * @property data\n     * @type {Object}\n     * @default null\n     */\n    data: null,\n    /**\n     * Spatials data\n     * @property spatials\n     * @type {Object}\n     * @default null\n     */\n    spatials: null,\n    /**\n     * Defines configuration\n     * @property settings\n     * @type {Object}\n     * @default null\n     */\n    settings: {},\n    /**\n     * In charge to render messages in the web console output\n     * @property logger\n     * @type {Object}\n     * @default null\n     */\n    logger: null,\n    /**\n     * Localisation manager\n     * @property localisationManager\n     * @type {Object}\n     * @default null\n     */\n    localisationManager: null,\n    /**\n     * In charge to inisialize this class\n     * @constructor\n     * @param {Object} settings\n     * @param {Object} data\n     */\n    init: function (settings) {\n        this.spatials = [];\n        this.settings = $.extend({\n                cuepointMinDuration: 0,\n                debug: false\n            },\n            settings || {});\n        this.localisationManager = new fr.ina.amalia.player.LocalisationManager();\n        if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n            this.logger = new fr.ina.amalia.player.log.LogHandler({\n                enabled: this.settings.debug\n            });\n        }\n    },\n    /**\n     * In charge to parse metadata data\n     * @method parserMetadata\n     * @param {Object} color\n     */\n    parserSpacialMetadata: function (data, metadataId, color) {\n        this.data = data;\n        this.spatials = [];\n        var item = null;\n        for (var i = 0;\n             i < this.data.length;\n             i++) {\n            item = this.data[i];\n            if (item.hasOwnProperty('sublocalisations') && item.sublocalisations !== null && item.sublocalisations.hasOwnProperty('localisation') && item.sublocalisations.localisation.length > 0) {\n                this.createSpatialItem(item, metadataId, color);\n            }\n            else if (item.hasOwnProperty('sublocalisations') && item.sublocalisations === null && item.hasOwnProperty('shape') && item.shape !== null) {\n                this.createSpatialItemPoint(item, metadataId, color);\n            }\n        }\n        if (this.logger !== null) {\n            this.logger.trace(this.Class.fullName, \"parserMetadata\");\n            this.logger.info(this.data);\n        }\n        return this.getData();\n    },\n    /**\n     * In charge to parse spatials block\n     * @param {Object} metadataId\n     * @param {Object} color\n     */\n    createSpatialItemPoint: function (localisation, metadataId, color) {\n        var startPos = localisation;\n        var endPos = $.extend(true, [], localisation);\n        endPos.tc += this.settings.cuepointMinDuration;\n        this.addSpatial(startPos, endPos, localisation, color);\n\n    },\n    /**\n     * In charge to parse spatials block\n     * @param {Object} data\n     * @param {Object} metadataId\n     * @param {Object} color\n     */\n    createSpatialItem: function (data, metadataId, color) {\n        this.localisationManager.setLoc(data.sublocalisations.localisation);\n        var locTc = this.localisationManager.getLocTc();\n        if (locTc !== null) {\n            //update main tc\n            data.tcin = locTc.tcin;\n            data.tcout = locTc.tcout;\n\n            var localisations = data.sublocalisations.localisation;\n            var startPos = null;\n            var endPos = null;\n            for (var i = 0;\n                 i < localisations.length;\n                 i++) {\n\n                if (startPos === null) {\n                    startPos = localisations[i];\n                }\n                else {\n                    endPos = localisations[i];\n                    this.addSpatial(startPos, endPos, data, metadataId, color);\n                    startPos = localisations[i];\n                }\n            }\n        }\n\n    },\n    /**\n     * In charge to add spacial data\n     * @param {Number} startPos\n     * @param {Number} endPos\n     * @param {Object} item\n     * @param {Object} metadataId\n     * @param {Object} color\n     */\n    addSpatial: function (startPos, endPos, item, metadataId, color) {\n        if (typeof startPos === \"object\" && typeof endPos === \"object\" && startPos.hasOwnProperty('tc') && endPos.hasOwnProperty('tc')) {\n            this.spatials.push({\n                start: startPos,\n                end: endPos,\n                type: this.getType(startPos),\n                data: item.data,\n                label: item.label,\n                thumb: item.thumb,\n                tcin: (typeof startPos.tc === \"string\") ? fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(startPos.tc) : startPos.tc,\n                tcout: (typeof endPos.tc === \"string\") ? fr.ina.amalia.player.helpers.UtilitiesHelper.convertHourToSeconde(endPos.tc) : endPos.tc,\n                refLoc: item,\n                metadataId: metadataId,\n                color: color\n            });\n        }\n        else {\n            if (this.logger !== null) {\n                this.logger.warn(this.Class.fullName + \": Error to add spatial\");\n                this.logger.warn([\n                    startPos,\n                    endPos\n                ]);\n            }\n        }\n    },\n    /**\n     * Return spatical object type\n     * @param {Object} data\n     * @return {String} rect/point/ellipse\n     */\n    getType: function (data) {\n        if (data.hasOwnProperty(\"shape\") && data.shape !== null && data.shape.hasOwnProperty('t') && typeof data.shape.t === \"string\") {\n            return data.shape.t.toString();\n        }\n        else {\n            return \"rectangle\";\n        }\n    },\n    /**\n     * Return spacial data with start and end position\n     * @returns {Object}\n     */\n    getData: function () {\n        return this.spatials;\n    }\n});\n"
  },
  {
    "path": "src/plugins/storyboard/base-frame-position-calculator.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to load data\n * @class BaseFramePositionCalculator\n * @namespace fr.ina.amalia.player\n * @module player\n * @constructor\n * @param {Object} parameter\n * @param {Object} mediaContainer\n * @param {Object} handlerData\n */\n$.Class(\"fr.ina.amalia.player.BaseFramePositionCalculator\", {\n\n    getFrameByTc: function (tc, duration, col, row, framePreviewHeight, framePreviewWidth) {\n        var line = Math.round(Math.max(1, Math.round((tc * row * col) / duration) / col));\n        return {\n            x: -Math.min(framePreviewWidth * ( Math.round(tc) % col), framePreviewWidth * (col - 1)),\n            y: -line * framePreviewHeight + framePreviewHeight\n        };\n    }\n}, {});\n"
  },
  {
    "path": "src/plugins/storyboard/plugin-storyboard.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to display storyboard\n * @class StoryboardPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-watermark\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.StoryboardPlugin\", {\n        classCss: \"ajs-plugin plugin-storyboard\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Thumbnail overlay canvas\n         * @property thumbnailOverlayCanvas\n         * @type {Object}\n         * @default []\n         */\n        thumbnailOverlayCanvas: null,\n        /**\n         * storyboard image obj\n         * @property storyboardImageObj\n         * @type {Object}\n         * @default []\n         */\n        storyboardImageObj: null,\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * Position calculator\n         * @property positionCalculator\n         * @type {Function}\n         * @default []\n         */\n        positionCalculator: null,\n        /**\n         * Initialize storyboard plugin and create container of this plugin\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.thumbnailOverlayCanvas = null;\n            this.settings = $.extend({\n                topOffset: 80,\n                framePositionCalculator: 'fr.ina.amalia.player.BaseFramePositionCalculator',\n                storyboardSourceFile: '',\n                framePreviewWidth: 160,\n                framePreviewHeight: 90,\n                storyboardRow: 36,\n                storyboardCol: 10,\n                fadeInDuration: 300,\n                fadeOutDuration: 600,\n                fadeInEffect: 'swing',\n                fadeOutEffect: 'easeInExpo',\n                filmstrip: true,\n                thumbScale: 1.5\n            }, this.settings.parameters || {});\n\n            if (this.settings.storyboardSourceFile !== '') {\n                this.container = $('<div>', {\n                    'class': this.Class.classCss\n                });\n                try {\n                    /* jslint evil: true */\n                    this.positionCalculator = eval(this.settings.framePositionCalculator + '.getFrameByTc');\n                }\n                catch (e) {\n                    this.positionCalculator = fr.ina.amalia.player.BaseFramePositionCalculator.getFrameByTc;\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Set Default position calculator\");\n                    }\n                }\n                this.storyboardImageObj = new Image();\n                this.storyboardImageObj.src = this.settings.storyboardSourceFile;\n                this.pluginContainer.append(this.container);\n                this.createStoryboardElement();\n                this.defineListeners();\n            }\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            // Player events\n            mainContainer.one(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this\n            }, this.onPluginReady);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.START_SEEKING, {\n                self: this\n            }, this.onStartSeeking);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SEEKING, {\n                self: this\n            }, this.onSeeking);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.STOP_SEEKING, {\n                self: this\n            }, this.onStopSeeking);\n            // call function 200 ms after resize is complete.\n            $(window).on('debouncedresize', {\n                self: this\n            }, this.onWindowResize);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * Initialize watermark plugin on load start event and create plugin\n         * container\n         * @method initializeOnLoadStart\n         * @returns {undefined}\n         */\n        initializeOnLoadStart: function () {\n            this.updateFramePreview(0);\n        },\n        /**\n         * In charge to create watermark element\n         * @method createWatermarkElement\n         */\n        createStoryboardElement: function () {\n            var storyboard = $('<div>', {\n                'class': 'preview-storyboard'\n            });\n            var thumbnail = $('<div>', {\n                'class': 'thumbnail'\n            });\n            thumbnail.css({\n                'bottom': this.settings.topOffset,\n                'height': this.settings.framePreviewHeight,\n                'width': this.settings.framePreviewWidth,\n                'margin-left': -this.settings.framePreviewWidth / 2,\n                'background-image': 'url(' + this.settings.storyboardSourceFile + ')'\n\n            });\n            //Filmstrip\n            if (this.settings.filmstrip === true) {\n                var filmstripContainer = $('<div>', {\n                    'class': 'filmstrip'\n                });\n                filmstripContainer.css({\n                    'bottom': this.settings.topOffset,\n                    'height': this.settings.framePreviewHeight\n                });\n                this.container.append(filmstripContainer);\n            }\n            var thumbnailOverlay = $('<canvas>', {\n                'class': 'thumbnail-overlay'\n            });\n            thumbnailOverlay.attr('width', this.settings.framePreviewWidth);\n            thumbnailOverlay.attr('height', this.settings.framePreviewHeight);\n            this.thumbnailOverlayCanvas = thumbnailOverlay.get(0).getContext('2d');\n            storyboard.append(thumbnailOverlay);\n            this.container.append(thumbnail);\n            this.container.append(storyboard);\n            this.updateFramePreview(0);\n        },\n        /**\n         * In charge to update frame preview\n         * @method updateFramePreview\n         */\n        updateFramePreview: function (percentage) {\n            var videoSize = this.getVideoSize();\n            var width = videoSize.w;\n            var height = videoSize.h;\n            var left = (this.container.width() - width) / 2;\n            var top = (this.container.height() - height - this.settings.topOffset) / 2;\n            this.container.find('.preview-storyboard').css({\n                width: width + 'px',\n                height: height + 'px',\n                top: top + 'px',\n                left: left + 'px'\n            });\n            var duration = this.mediaPlayer.getDuration();\n            var tc = percentage * 10 * duration / 1000;\n            if (this.positionCalculator !== null && typeof this.positionCalculator !== \"undefined\") {\n                var pos = this.positionCalculator(tc, duration, this.settings.storyboardCol, this.settings.storyboardRow, this.settings.framePreviewHeight, this.settings.framePreviewWidth);\n                this.container.find('.thumbnail').css({'background-position': Math.round(pos.x) + 'px ' + Math.round(pos.y) + 'px'});\n                this.thumbnailOverlayCanvas.drawImage(this.storyboardImageObj, pos.x, pos.y, this.settings.framePreviewWidth * this.settings.storyboardCol, this.settings.framePreviewHeight * this.settings.storyboardRow);\n                //update filmstripPos\n                if (this.settings.filmstrip === true) {\n                    this.updateFilmstrip(percentage * 10);\n                }\n            }\n        },\n        /**\n         * In charge to refresh filmstrip container\n         * @param percentage\n         */\n        updateFilmstrip: function (percentage) {\n            var maxItem = Math.ceil(this.container.width() / this.settings.framePreviewWidth);\n            var duration = this.mediaPlayer.getDuration();\n            var startPercent = Math.max(0, percentage - maxItem);\n            var endPercent = Math.min(1000, percentage + maxItem);\n            this.container.find('.filmstrip').empty();\n            var leftPos = this.container.width() / 2 - ( this.settings.framePreviewWidth / 2);\n            for (var leftPercent = percentage; leftPercent > startPercent; leftPercent--) {\n                var tcLeft = leftPercent * duration / 1000;\n                var getPosLeft = this.positionCalculator(tcLeft, duration, this.settings.storyboardCol, this.settings.storyboardRow, this.settings.framePreviewHeight, this.settings.framePreviewWidth);\n                var filmstripItemLeft = $('<div>', {'class': 'storyboard-thumbnail left'});\n                filmstripItemLeft.css({\n                    'background-position': getPosLeft.x + 'px ' + getPosLeft.y + 'px',\n                    'left': leftPos + 'px',\n                    'height': this.settings.framePreviewHeight,\n                    'width': this.settings.framePreviewWidth,\n                    'background-image': 'url(' + this.settings.storyboardSourceFile + ')'\n                });\n                this.container.find('.filmstrip').append(filmstripItemLeft);\n                leftPos -= this.settings.framePreviewWidth;\n            }\n            var rightPos = this.container.width() / 2 - ( this.settings.framePreviewWidth / 2);\n            for (var rightPercent = percentage; rightPercent < endPercent; rightPercent++) {\n                var tcRight = rightPercent * duration / 1000;\n                var getPosRight = this.positionCalculator(tcRight, duration, this.settings.storyboardCol, this.settings.storyboardRow, this.settings.framePreviewHeight, this.settings.framePreviewWidth);\n                var filmstripItemRight = $('<div>', {'class': 'storyboard-thumbnail right'});\n                filmstripItemRight.css({\n                    'background-position': getPosRight.x + 'px ' + getPosRight.y + 'px',\n                    'left': rightPos + 'px',\n                    'height': this.settings.framePreviewHeight,\n                    'width': this.settings.framePreviewWidth,\n                    'background-image': 'url(' + this.settings.storyboardSourceFile + ')'\n                });\n                this.container.find('.filmstrip').append(filmstripItemRight);\n                rightPos += this.settings.framePreviewWidth;\n            }\n        },\n        /**\n         * Return video size\n         * @method getVideoSize\n         * @return {Object}\n         */\n        getVideoSize: function () {\n            var player = this.mediaPlayer.getMediaPlayer();\n            var videoHeight = player.get(0).videoHeight;\n            var videoWidth = player.get(0).videoWidth;\n            if (videoHeight === 0) {\n                videoHeight = player.height();\n            }\n            if (videoWidth === 0) {\n                videoWidth = player.width();\n            }\n            var widthRatio = player.width() / videoWidth;\n            var heightRatio = player.height() / videoHeight;\n            var ratio = Math.min(widthRatio, heightRatio);\n            return {\n                w: ratio * videoWidth,\n                h: ratio * videoHeight\n            };\n        },\n        /** Player Events * */\n        /**\n         * Fired when plugin ready event\n         * @method onPluginReady\n         * @param {Object} event\n         */\n        onPluginReady: function (event) {\n            event.data.self.initializeOnLoadStart();\n        },\n        /**\n         * Fired on windows resize event\n         * @method onWindowResize\n         * @param {Object} event\n         */\n        onWindowResize: function (event) {\n            event.data.self.updateFramePreview();\n        },\n        /**\n         * Fired on seeking event\n         * @method onStartSeeking\n         * @param {Object} event\n         */\n        onStartSeeking: function (event) {\n            event.data.self.container.fadeIn({\n                duration: event.data.self.settings.fadeInDuration,\n                easing: event.data.self.settings.fadeInEffect\n            });\n        },\n        /**\n         * Fired on seeking event\n         * @method onSeeking\n         * @param {Object} event\n         */\n        onSeeking: function (event, data) {\n            event.data.self.updateFramePreview(data.percentage);\n        },\n        /**\n         * Fired on seeking event\n         * @method onStopSeeking\n         * @param {Object} event\n         */\n        onStopSeeking: function (event) {\n            event.data.self.container.fadeOut({\n                duration: event.data.self.settings.fadeOutDuration,\n                easing: event.data.self.settings.fadeOutEffect\n            });\n        }\n    });"
  },
  {
    "path": "src/plugins/text-sync/component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to text sync block element\n * @class Component\n * @namespace fr.ina.amalia.player.components\n * @module plugin\n * @submodule plugin-text-sync\n * @constructor\n * @param {Object} settings\n * @param {Object} container\n */\n$.Class(\"fr.ina.amalia.player.plugins.textSyncPlugin.Component\", {\n        ComponentClassCss: \"line-component\",\n        eventTypes: {\n            CLICK: \"fr.ina.amalia.player.plugins.textSyncPlugin.Component.event.click\"\n        }\n    },\n    {\n        /**\n         * Defines configuration\n         * @property settings\n         * @type {Object}\n         * @default {}\n         */\n        settings: {},\n        /**\n         * In charge to render messages in the web console output\n         * @property logger\n         * @type {Object}\n         * @default null\n         */\n        logger: null,\n        /**\n         * Component container\n         * @property mainContainer\n         * @type {Object}\n         * @default null\n         */\n        mainContainer: null,\n        /**\n         * Tootip configuration\n         * @property tooltipConfiguration\n         * @type {Object}\n         * @default {}\n         */\n        tooltipConfiguration: {\n            position: {\n                my: \"left+10 center\",\n                at: \"right center\",\n                delay: 3000,\n                using: function (position, feedback) {\n                    $(this).css(position);\n                    $(\"<div>\").addClass(feedback.vertical).addClass(feedback.horizontal).appendTo(this);\n                }\n            }\n        },\n        /**\n         * In charge to init class\n         * @constructor\n         * @method init\n         * @param {Object} settings\n         */\n        init: function (settings) {\n            this.settings = $.extend({\n                    debug: false,\n                    framerate: '25',\n                    container: ''\n                },\n                settings || {});\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.mainContainer = this.settings.container;\n            if (this.mainContainer.length === 0 || typeof this.mainContainer !== \"object\") {\n                throw new Error(\"Your container is empty.\");\n            }\n            this.initialize();\n        },\n        /**\n         * Initialize the class and add style name to the main container\n         * @method initialize\n         */\n        initialize: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n            // Default class\n            this.mainContainer.addClass(this.Class.ComponentClassCss);\n        },\n        /**\n         * In charge to create one line of text-sync component\n         * @method createLine\n         * @param {Number} tcin\n         * @param {Number} tcout\n         * @param {String} label\n         * @param {String} text\n         * @param {String} thumb\n         */\n        createLine: function (tcin, tcout, label, text, thumb,metadata) {\n            var line = $('<li>', {\n                'class': 'line media',\n                'data-tcin': tcin,\n                'data-tcout': tcout\n            });\n            var leftBlock = $('<div>', {\n                class: 'info pull-left'\n            });\n            // thumb\n            thumb = (thumb !== null && typeof thumb !== \"undefined\" ) ? 'background-image:url(\"' + thumb + '\");' : '';\n            var imgContainer = $('<div>', {\n                class: 'thumb',\n                'data-tc': tcin,\n                style: thumb\n            });\n            leftBlock.append(imgContainer);\n            imgContainer.on('click', {\n                self: this\n            }, this.onClickAtTc);\n\n            var tcinContainer = $('<time>', {\n                class: 'tcin badge',\n                title: 'Tc in',\n                'data-tc': tcin,\n                text: fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tcin, this.settings.framerate, \"s\")\n            }).tooltip(this.tooltipConfiguration).on('click', {\n                self: this\n            }, this.onClickAtTc);\n\n            var tcoutContainer = $('<time>', {\n                class: 'tcout badge',\n                title: 'Tc out',\n                'data-tc': tcout,\n                text: fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tcout, this.settings.framerate, \"s\")\n            }).tooltip(this.tooltipConfiguration).on('click', {\n                self: this\n            }, this.onClickAtTc);\n\n            var progressbarContainer = $('<div>', {\n                class: 'ajs-progress'\n            }).hide();\n            var progressBar = $('<div>', {\n                class: 'ajs-progress-bar',\n                style: 'width:0%;'\n            });\n            progressbarContainer.append(progressBar);\n            leftBlock.append(tcinContainer);\n            leftBlock.append(tcoutContainer);\n            var contentBlock = $('<div>', {\n                'class': 'content'\n            });\n            if (label !== null) {\n                var labelContainer = $('<label>', {\n                    'class': 'heading',\n                    text: label\n                });\n                contentBlock.append(labelContainer);\n            }\n            this.textContainer = $('<p>', {\n                'class': 'text'\n            });\n            if (typeof text === 'string') {\n                this.textContainer.html(text);\n            }\n            else {\n                this.textContainer.append(text);\n            }\n\n            contentBlock.append(this.textContainer);\n            contentBlock.append(progressbarContainer);\n            line.append(leftBlock);\n            line.append(contentBlock);\n            line.data('metadata',metadata);\n            this.mainContainer.append(line);\n        },\n        /**\n         * In charge to add text in text container\n         * @method addText\n         * @param {String} text\n         */\n        addText: function (text) {\n            if (this.textContainer !== null) {\n                this.textContainer.append(text);\n                this.textContainer.find('.word').on('click', {\n                    self: this\n                }, this.onClickAtTc);\n            }\n        },\n        /** events* */\n        /**\n         * Fired when click event of text container\n         * @method onClickAtSegment\n         * @param {Object} event\n         * @event fr.ina.amalia.player.components.CuepointsComponent.eventTypes.CLICK\n         */\n        onClickAtTc: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tc = parseFloat(currentTarget.attr('data-tc'));\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.CLICK, {\n                tc: tc\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickAtCuepoint tc:\" + tc);\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/text-sync/text-sync.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to text sync plugin\n * @class TextSyncPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-text-sync\n * @constructor\n * @extends fr.ina.amalia.player.plugins.captions.CaptionsBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.captions.CaptionsBase.extend(\"fr.ina.amalia.player.plugins.TextSyncPlugin\", {\n        classCss: \"ajs-plugin plugin-text-sync\",\n        style: \"\"\n    },\n    {\n        /**\n         * Enable Main level\n         * @property withMainLevel\n         * @type {Object}\n         * @default null\n         */\n        withMainLevel: false,\n        /**\n         * True if mode karaoke is enabled\n         * @property karaoke\n         * @default null\n         */\n        karaoke: false,\n        /**\n         * true if load data started anw\n         * @property loadDataStarted\n         * @default null\n         */\n        loadDataStarted: false,\n        /**\n         * Selected metadata id provide by player core.\n         * @property loadDataStarted\n         * @default ''\n         */\n        selectedMetadataId: '',\n        initialize: function () {\n            this.container = $('<ul>', {\n                class: 'ajs-media-list'\n            });\n            this.settings = $.extend({\n                    debug: this.settings.debug,\n                    framerate: '25',\n                    internalPlugin: false,\n                    metadataId: '',\n                    level: 2,\n                    title: '',\n                    description: '',\n                    displayLevel: '',\n                    autoScroll: false,\n                    karaokeOffsetTime: 1\n                },\n                this.settings.parameters || {});\n            this.settings.displayLevel = (this.settings.displayLevel === \"\") ? this.settings.level : this.settings.displayLevel;\n            this.withMainLevel = (this.settings.displayLevel !== this.settings.level);\n            this.karaoke = this.withMainLevel;\n            if (this.settings.title !== '') {\n                this.createTitleBlock(this.settings.title, this.settings.description);\n            }\n\n            // events\n            this.container.on(fr.ina.amalia.player.plugins.textSyncPlugin.Component.eventTypes.CLICK, {\n                self: this\n            }, this.onClickTc);\n\n            this.pluginContainer.append(this.container);\n            this.defineEventListeners();\n        },\n        /**\n         * In charge to define event listener\n         * @method defineEventListeners\n         */\n        defineEventListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE, {\n                self: this\n            }, this.onBeginDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                self: this\n            }, this.onDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE, {\n                self: this\n            }, this.onEndDataChange);\n\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this\n            }, this.onTimeupdate);\n            if (this.settings.autoScroll === true) {\n                mainContainer.on(fr.ina.amalia.player.PlayerEventType.SEEK, {\n                    self: this\n                }, this.onSeek);\n            }\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n        },\n        /**\n         * In charge to update display items\n         * @method updateListOfDisplayItems\n         * @param {Number} displayLevel\n         */\n        updateListOfDisplayItems: function (displayLevel) {\n            var component = null;\n            var data = null;\n            // Clear old data\n            this.container.empty();\n            for (var i = 0;\n                 i < this.listOfData.length;\n                 i++) {\n                data = this.listOfData[i];\n                if (data.level === displayLevel) {\n                    component = this.createComponent(this.container);\n                    if (component !== null) {\n                        if (typeof component.createLine === \"function\") {\n                            component.createLine(data.tcin, data.tcout, data.label, (this.withMainLevel === true) ? '' : data.text, data.thumb, data);\n                        }\n                    }\n                    else {\n                        if (this.logger !== null) {\n                            this.logger.warn(\"Error initializing the component.\");\n                        }\n                    }\n                }\n                else  if (data.level > displayLevel)  {\n                    if (component !== null) {\n                        component.addText(this.createWord(data));\n                    }\n                }\n            }\n            // Définie la hauteur du container\n            this.container.height(parseInt(this.pluginContainer.height() - parseInt(this.pluginContainer.find(\".header\").first().height()) - 20) + \"px\");\n        },\n        /**\n         * In charge to create word element\n         * @param {Object} data\n         */\n        createWord: function (data) {\n            return $('<span>', {\n                'class': 'word',\n                'text': data.text,\n                'data-tc': data.tcin,\n                'data-tcin': data.tcin,\n                'data-tcout': data.tcout\n            }).data('metadata', data);\n        },\n        /**\n         * In charge to create text sync component\n         * @param {String} container\n         * @param {Object} settings\n         */\n        createComponent: function (container, settings) {\n            var component = null;\n            var componentSettings = $.extend({\n                debug: this.settings.debug,\n                container: container\n            }, settings || {});\n\n            try {\n                component = new fr.ina.amalia.player.plugins.textSyncPlugin.Component(componentSettings);\n            }\n            catch (e) {\n                if (this.logger !== null) {\n                    this.logger.warn(e);\n                }\n            }\n            return component;\n        },\n        /**\n         * In charge to update positions\n         * @method updatePos\n         * @param {Object} currentTime\n         */\n        updatePos: function (currentTime) {\n\n            currentTime = parseFloat(currentTime);\n            // Line\n            this.container.find('.line').removeClass('on');\n            this.container.find('.line').filter(function (index, element) {\n                return (currentTime >= Math.round(parseFloat($(element).attr('data-tcin'))) && currentTime < Math.round(parseFloat($(element).attr('data-tcout'))));\n            }).addClass('on').each(function (index, element) {\n                element = $(element);\n                var tcin = Math.round(parseInt($(element).attr('data-tcin')));\n                var tcout = Math.round(parseInt($(element).attr('data-tcout')));\n                var percentWidth = ((Math.round(currentTime) - tcin) * 100) / (tcout - tcin);\n                element.find('.ajs-progress').show();\n                element.find('.ajs-progress-bar').css('width', Math.round(percentWidth) + '%');\n            });\n            // Word\n            if (this.karaoke === true) {\n                this.container.find('.word').removeClass('on');\n                var self = this;\n                this.container.find('.word').filter(function (index, element) {\n                    return (currentTime >= parseFloat($(element).attr('data-tcin')) && currentTime < parseFloat($(element).attr('data-tcout')) + self.settings.karaokeOffsetTime);\n                }).addClass('on');\n            }\n            if (this.settings.autoScroll === true) {\n                var elementPosition = this.container.find('li.line').eq(0).position();\n                var offsetPosition = this.container.find('li.line.on').position();\n                if (elementPosition && offsetPosition) {\n                    this.container.stop().animate({\n                        scrollTop: offsetPosition.top - elementPosition.top\n                    });\n                }\n            }\n        },\n        /**\n         * In charge to create tile block\n         * @param {Object} title\n         * @param {Object} description\n         */\n        createTitleBlock: function (title, description) {\n            var container = $('<div>', {\n                class: \"header\"\n            });\n            var containerBody = $('<div>', {\n                class: \"resume\"\n            });\n            var titleContainer = $('<h3>', {\n                class: \"heading\",\n                text: title\n            });\n            containerBody.append(titleContainer);\n            var descriptionContaienr = $('<p>', {\n                text: description\n            });\n            containerBody.append(descriptionContaienr);\n            container.append(containerBody);\n            this.pluginContainer.append(container);\n        },\n        /**\n         * In charge to update block data\n         */\n        updateBlockData: function () {\n            // use settings metadata if not empty\n            if (this.settings.metadataId !== '') {\n                this.updateMetadata(this.settings.metadataId, this.settings.level, 0, this.mediaPlayer.getDuration(), true);\n                this.updateListOfDisplayItems(this.settings.displayLevel);\n            }\n            else if (this.selectedMetadataId !== '') {\n                this.updateMetadata(this.selectedMetadataId, this.settings.level, 0, this.mediaPlayer.getDuration(), true);\n                this.updateListOfDisplayItems(this.settings.displayLevel);\n            }\n        },\n        /**\n         * Fired on click event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onClickTc: function (event, data) {\n            if (data.hasOwnProperty('tc') === true && data.tc !== true) {\n                event.data.self.mediaPlayer.setCurrentTime(parseFloat(data.tc));\n            }\n        },\n        /**\n         * In charge of seek events\n         * @method onSeek\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSeek: function (event, data) {\n            var currentTime = (parseFloat(data.currentTime));\n            var offsetPosition = event.data.self.container.find('li.line').eq(0).position();\n            var elementPosition = event.data.self.container.find('li.line').filter(function (index, element) {\n                return (currentTime >= Math.round(parseFloat($(element).attr('data-tcin'))) && currentTime < Math.round(parseFloat($(element).attr('data-tcout'))));\n            }).first().position();\n            if (elementPosition && offsetPosition) {\n                event.data.self.container.stop().animate({\n                    scrollTop: elementPosition.top - offsetPosition.top\n                }, 1500, 'easeInOutExpo');\n            }\n        },\n        /**\n         * Fired on selected metadata change\n         * @method onSelectedMetadataChange\n         */\n        onSelectedMetadataChange: function (event, data) {\n            if (event.data.self.settings.metadataId === '' && data.metadataId !== null) {\n                event.data.self.selectedMetadataId = data.metadataId.toString();\n                event.data.self.updateBlockData();\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectedMetadataChange id:\" + data.metadataId);\n            }\n        },\n        /**\n         * Fired on begin data change event\n         * @method onBeginDataChange\n         * @param {Object} event\n         */\n        onBeginDataChange: function (event) {\n            event.data.self.loadDataStarted = true;\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onBeginDataChange\");\n            }\n        },\n        /**\n         * Fired on data change event\n         * @param {Object} event\n         */\n        onDataChange: function (event) {\n            if (event.data.self.loadDataStarted === false) {\n                event.data.self.updateBlockData();\n            }\n        },\n        /**\n         * Fired on end data change event\n         * @method onEndDataChange\n         * @param {Object} event\n         */\n        onEndDataChange: function (event) {\n            event.data.self.loadDataStarted = false;\n            event.data.self.updateBlockData();\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"EndDataChange\");\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/base-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class on all components used in timeline plugin\n * @class BaseComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @param {Object} settings\n * @param {Object} container\n */\n$.Class(\"fr.ina.amalia.player.plugins.timeline.BaseComponent\", {\n        ComponentClassCss: \"default-component\",\n        ComponentModuleClassCss: \"module-default\",\n        STYLE_CLASSNAME_EXPAND_ON: 'ajs-icon-chevron-down',\n        STYLE_CLASSNAME_EXPAND_OFF: 'ajs-icon-chevron-right',\n        LocalStorageTimeDisplayStateNamespace: 'timeline-display-state',\n        NAV_CLICK: 'fr.ina.amalia.player.plugins.timeline.event.NavClick',\n        CLOSE_EVENT: 'fr.ina.amalia.player.plugins.timeline.component.event.CloseEvent',\n        CLICK_TC: 'fr.ina.amalia.player.plugins.timeline.BaseComponent.event.click',\n        CLICK_SELECT: 'fr.ina.amalia.player.plugins.timeline.BaseComponent.event.click_select',\n        CLICK_REMOVE_SELECT_ITEM: 'fr.ina.amalia.player.plugins.timeline.BaseComponent.event.click_remove_select_item'\n    },\n    {\n        /**\n         * Logger\n         * @property logger\n         * @type {fr.ina.amalia.player.log.LogHandler} Logger\n         * @default null\n         */\n        logger: null,\n        /**\n         * Component settings\n         * @property settings\n         * @type {Object} Logger\n         * @default {}\n         */\n        settings: {},\n        /**\n         * Main container of this component\n         * @property mainContainer\n         * @type {Object}\n         * @default null\n         */\n        mainContainer: null,\n        /**\n         * Tooltip configuration\n         * @property tooltipConfiguration\n         * @type {Object}\n         * @default null\n         */\n        tooltipConfiguration: {},\n        /**\n         * zoom level\n         * @property zoomLevel\n         * @type {Object}\n         * @default null\n         */\n        zoomLevel: 0,\n        /**\n         * Start time code\n         * @property tcin\n         * @type {Number}\n         * @default 0\n         */\n        tcin: 0,\n        /**\n         * End time code\n         * @property tcout\n         * @type {Number}\n         * @default 0\n         */\n        tcout: 0,\n        /**\n         * Duration\n         * @property duration\n         * @type {Number}\n         * @default 0\n         */\n        duration: 0,\n        /**\n         * Component display state\n         * @property displayStateList\n         * @type {Array}\n         * @default []\n         */\n        displayStateList: [\n            'lg',\n            'md',\n            'sm',\n            'xs'\n        ],\n        /**\n         * Display state index\n         * @property displayStateIdx\n         * @type {Array}\n         * @default []\n         */\n        displayStateIdx: 0,\n        /**\n         * Instance of local storage manager\n         * @property localStorageManager\n         * @type {Object}\n         * @default null\n         */\n        localStorageManager: null,\n        /**\n         * Attribute in charge of zoom\n         * @property displayStateIdx\n         * @type {Array}\n         * @default []\n         */\n        zoomProperty: null,\n        /**\n         * Focus component\n         * @property displayStateIdx\n         * @type {Array}\n         * @default []\n         */\n        focusable: null,\n        /**\n         * True if the component has elements\n         * @property hasElements\n         * @type {Boolean}\n         * @default false\n         */\n        hasElements: false,\n        /**\n         * True if the component is zoomable\n         * @property zoomable\n         * @type {Boolean}\n         * @default false\n         */\n        zoomable: false,\n        /**\n         * Focus component\n         * @property focusComponent\n         * @type {Object}\n         * @default null\n         */\n        focusComponent: null,\n        /**\n         * focus line\n         * @property focusLine\n         * @type {Object}\n         * @default null\n         */\n        focusLine: null,\n        /**\n         * list of data\n         * @property listOfdata\n         * @type {Array}\n         * @default null\n         */\n        listOfdata: null,\n        /**\n         * Zoom component\n         * @property zoomableZone\n         * @type {Array}\n         * @default []\n         */\n        zoomableZone: null,\n        /**\n         * Start time code for zoom\n         * @property zTcin\n         * @type {Number}\n         * @default 0\n         */\n        zTcin: 0,\n        /**\n         * End time code for zoom\n         * @property zTcout\n         * @type {Number}\n         * @default 0\n         */\n        zTcout: 0,\n        /**\n         * Display state index\n         * @property currentTime\n         * @type {Number}\n         * @default 0\n         */\n        currentTime: 0,\n        /**\n         * Id of metadata managed by this plugin\n         * @property metadataId\n         * @type {Number}\n         * @default 0\n         */\n        metadataId: '',\n        /**\n         * Id of metadata managed by this plugin\n         * @property metadataId\n         * @type {Number}\n         * @default 0\n         */\n        tcOffset: 0,\n        /**\n         * in charge to manage localisations\n         * @property localisationManager\n         * @type {Object}\n         * @default null\n         */\n        localisationManager: null,\n        mediaPlayer: null,\n        /**\n         * Init this class\n         * @method init\n         * @param settings\n         */\n        init: function (settings, mediaPlayer) {\n            this.mediaPlayer = mediaPlayer;\n            this.tooltipConfiguration = $.extend(true, fr.ina.amalia.player.plugins.timeline.DefaultConfiguration.tooltipConfiguration, {});\n            this.settings = $.extend({\n                    debug: false,\n                    icon: 'circle',\n                    color: \"#3cf\",\n                    container: '',\n                    metadataId: '',\n                    tcOffset: 0,\n                    callbacks: {},\n                    duration: 0,\n                    pointNav: false,\n                    zoomHierarchies: false,\n                    overlap: false,\n                    withParentLevel: false,\n                    zoomable: true,\n                    focusable: false,\n                    toolsbar: true,\n                    editable: false,\n                    closeable: false,\n                    viewZoom: false,\n                    marker: false,\n                    timecursor: false,\n                    thumbDirectory: '',\n                    timeFormat: 'mms',\n                    framerate: 25,\n                    displayState: '',\n                    callback: '',\n                    callbackStyle: ''\n                },\n                settings || {});\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.mainContainer = this.settings.container;\n            this.localStorageManager = new fr.ina.amalia.player.LocalStorageManager({});\n            this.focusable = this.settings.focusable;\n            this.zoomable = this.settings.zoomable;\n            this.duration = parseFloat(this.settings.duration);\n            this.tcOffset = parseFloat(this.settings.tcOffset);\n            this.zTcin = parseFloat(this.tcOffset);\n            this.zTcout = parseFloat(this.tcOffset + this.settings.duration);\n            this.localisationManager = new fr.ina.amalia.player.LocalisationManager();\n\n            if (this.mainContainer.length === 0 || typeof this.mainContainer !== \"object\") {\n                throw new Error(\"Your container is empty.\");\n            }\n            this.initialize();\n        },\n        /**\n         * In charge to initialize the base component.\n         * @method initialize\n         */\n        initialize: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n            // Default class\n            this.mainContainer.addClass(this.Class.ComponentClassCss);\n            this.createAxis();\n            if (this.settings.toolsbar === true) {\n                this.createToolBar();\n            }\n            if (this.settings.focusable === true) {\n                this.createBottomToolsBar();\n            }\n            if (this.settings.closeable === true) {\n                this.createCloseToolBar();\n            }\n            if (this.settings.timecursor === true) {\n                this.createTimeCursor();\n                this.mainContainer.parents('.ajs-plugin.plugin-timeline').first().on(fr.ina.amalia.player.plugins.TimelinePlugin.eventTypes.TIME_CHANGE, {\n                    self: this\n                }, this.onPluginTimeChange);\n            }\n\n            if (this.settings.focusable === false && this.settings.zoomable === false && this.settings.viewZoom === true) {\n                this.createZoomContainer();\n            }\n            if (typeof this.settings.callbacks.onCreated !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(this.settings.callbacks.onCreated + '(this)');\n                }\n                catch (e) {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Error to create callback.\");\n                    }\n                }\n            }\n        },\n        /**\n         * Create time axe container\n         * @method createAxis\n         */\n        createAxis: function () {\n            var line = $('<div>', {\n                'class': 'line'\n            });\n            var lineContent = $('<div>', {\n                'class': 'line-content'\n            });\n            lineContent.append(line);\n            var axis = $('<div>', {\n                class: this.Class.ComponentModuleClassCss\n            });\n            axis.append(lineContent);\n            if (this.settings.callback !== \"\") {\n                var callbackBtn = $('<div>', {\n                    class: 'callback ' + this.settings.callbackStyle\n                });\n                callbackBtn.on('click', {\n                    self: this\n                }, this.onClickToCallback);\n                axis.append(callbackBtn);\n            }\n            var label = $('<div>', {\n                class: 'label',\n                text: this.settings.title\n            });\n            axis.append(label);\n            //Add callback event\n            if (this.settings.callback !== \"\") {\n                label.on('click', {\n                    self: this\n                }, this.onClickToCallback);\n            }\n\n            // Add to main container\n            this.mainContainer.append(axis);\n            var displayState = this.settings.displayState;\n            if (displayState === \"\") {\n                displayState = this.localStorageManager.getItem(this.Class.LocalStorageTimeDisplayStateNamespace);\n            }\n            if (displayState === null) {\n                displayState = this.getDisplayState();\n            }\n            else {\n                this.displayStateIdx = this.displayStateList.indexOf(displayState);\n                this.displayStateIdx++;\n            }\n            this.mainContainer.removeClass(this.displayStateList.toString().replace(/,/g, ' ')).addClass(displayState);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"createAxis\");\n            }\n        },\n        /**\n         * Create tool bar for component with controls\n         * @method createToolBar\n         */\n        createToolBar: function () {\n            var toolbarContainer = $('<div>', {\n                'class': 'toolbar-container'\n            });\n            // expend bouton\n            var expandBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_EXPAND, \"expand-btn\");\n            expandBtn.on('click', {\n                self: this\n            }, this.onClickExpandBtn);\n            expandBtn.addClass(this.Class.STYLE_CLASSNAME_EXPAND_ON);\n            toolbarContainer.append(expandBtn);\n            // sort bouton\n            var sortBtn = $('<div>', {\n                title: fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SORT,\n                'class': 'sort-btn ajs-icon ajs-icon-sort'\n            }).tooltip(this.tooltipConfiguration);\n            toolbarContainer.append(sortBtn);\n            // module de navigation par point\n            if (this.settings.pointNav === true) {\n                // /Navigation controls\n                var navControls = $('<div>', {\n                    'class': 'nav-controls'\n                });\n                // Prev\n                var prevNavContorl = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_NAV_POINT_PREV, \"chevron-circle-left prev-control\");\n                prevNavContorl.on('click', {\n                        self: this\n                    },\n                    this.onClickPrevNavContorl);\n                navControls.append(prevNavContorl);\n                // Next\n                var nextNavContorl = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_NAV_POINT_NEXT, \"chevron-circle-right next-control\");\n                nextNavContorl.on('click', {\n                        self: this\n                    },\n                    this.onClickNextNavContorl);\n                navControls.append(nextNavContorl);\n                toolbarContainer.append(navControls);\n            }\n\n            this.mainContainer.append(toolbarContainer);\n            return toolbarContainer;\n        },\n        /**\n         * Create close button\n         * @method createCloseToolBar\n         */\n        createCloseToolBar: function () {\n            var closeToolbarContainer = $('<div>', {\n                'class': 'toolbar-container'\n            });\n            // expend button\n            var closeBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_CLOSE, \"remove\");\n            closeBtn.on('click', {\n                    self: this\n                },\n                this.onClickCloseBtn);\n            closeToolbarContainer.append(closeBtn);\n            this.mainContainer.append(closeToolbarContainer);\n        },\n        /**\n         * Create time cursor container\n         * @method createTimeCursor\n         */\n        createTimeCursor: function () {\n            var timeCursor = $('<div>', {\n                'class': 'timecursor',\n                'left': '0px'\n            });\n            this.mainContainer.append(timeCursor);\n        },\n        /**\n         * Create button with tool tip\n         * @param {String} name\n         * @param {String} icon\n         * @param {Boolean} tooltip\n         */\n        createButton: function (name, icon, tooltip) {\n            if (tooltip === false) {\n                return $('<button>', {\n                    value: name,\n                    class: icon + \" ajs-icon ajs-icon-\" + icon\n                });\n            }\n            else {\n                return $('<button>', {\n                    title: name,\n                    class: icon + \" plugin-btn ajs-icon ajs-icon-\" + icon\n                }).tooltip(this.tooltipConfiguration);\n            }\n        },\n        /**\n         * Create bottom tool bar container\n         * @method createBottomToolsBar\n         */\n        createBottomToolsBar: function () {\n            var bottomToolbarContainer = $('<div>', {\n                'class': 'bottom-toolbar-container'\n            });\n            if (this.settings.focusable === true) {\n                var focusZoomBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_FOCUS, \"zoom-in focus-btn\");\n                focusZoomBtn.on('click', {\n                    self: this\n                }, this.onClickFocusBtn);\n                bottomToolbarContainer.append(focusZoomBtn);\n            }\n            this.mainContainer.append(bottomToolbarContainer);\n        },\n        /**\n         * In charge to create focus component\n         * @method createFocusComponent\n         */\n        createFocusComponent: function () {\n            var focusSettings = {\n                debug: this.settings.debug,\n                tcOffset: (typeof this.mediaPlayer === \"undefined\") ? 0 : this.mediaPlayer.getTcOffset()\n            };\n\n            this.focusComponent = new fr.ina.amalia.player.plugins.components.FocusComponent(this.mainContainer, this.duration, focusSettings);\n            this.focusComponent.setTc(this.tcin, this.tcout);\n            try {\n                var focusLineContainer = $('<div>', {\n                    class: 'sub',\n                    style: ''\n                });\n                this.mainContainer.append(focusLineContainer);\n                var focusLineSettings = jQuery.extend(true, {}, this.settings);\n                focusLineSettings.container = focusLineContainer;\n                focusLineSettings.focusable = false;\n                focusLineSettings.pointNav = false;\n                focusLineSettings.title = \"\";\n                focusLineSettings.toolsbar = false;\n                focusLineSettings.zoomHierarchies = false;\n                focusLineSettings.withParentLevel = false;\n                focusLineSettings.zoomable = true;\n                focusLineSettings.closeable = true;\n                focusLineSettings.color = \"#FFF\";\n                focusLineSettings.viewZoom = false;\n                /* jslint evil: true */\n                this.focusLine = eval(\"new \" + this.Class.fullName + '(focusLineSettings)');\n                // set default tcin and tcout\n                this.focusLine.setTcin(this.focusComponent.getFocusTcin());\n                this.focusLine.setTcout(this.focusComponent.getFocusTcout());\n\n                this.mainContainer.on(fr.ina.amalia.player.plugins.components.FocusComponent.eventTypes.FOCUS_ZONE_CHANGE, {\n                        self: this\n                    },\n                    this.onFocusZoneChange);\n                this.focusLine.mainContainer.on(this.Class.CLOSE_EVENT, {\n                        self: this\n                    },\n                    this.onCloseEventRecever);\n\n                if (this.focusLine !== null) {\n                    this.focusLine.removeItems();\n                    this.focusLine.addItems(this.listOfdata);\n                }\n            }\n            catch (e) {\n                if (this.logger !== null) {\n                    this.logger.warn(e);\n                }\n            }\n        },\n        /**\n         * In charge to create zoom container\n         * @method createZoomContainer\n         */\n        createZoomContainer: function () {\n            try {\n                var focusSettings = {\n                    debug: this.settings.debug,\n                    modeFocus: false,\n                    tcOffset: this.settings.tcOffset\n                };\n                this.zoomableZone = new fr.ina.amalia.player.plugins.components.FocusComponent(this.mainContainer, this.duration, focusSettings);\n                this.zoomableZone.setTc(this.zTcin, this.zTcout);\n            }\n            catch (e) {\n                if (this.logger !== null) {\n                    this.logger.warn(e);\n                }\n            }\n\n        },\n        /**\n         * Show this component\n         * @method show\n         */\n        show: function () {\n            this.mainContainer.show();\n        },\n        /**\n         * Hide this component\n         * @method hide\n         */\n        hide: function () {\n            this.mainContainer.hide();\n        },\n        /**\n         * return main container\n         * @method getContainer\n         * @returns {Object}\n         */\n        getContainer: function () {\n            return this.mainContainer;\n        },\n        /**\n         * Return metadata id\n         * @method getMetadataId\n         */\n        getMetadataId: function () {\n            return this.metadataId.toString();\n        },\n        /**\n         * Set line title\n         * @param title\n         */\n        setTitle: function (title) {\n            this.settings.title = title;\n            this.mainContainer.find('.label').text(title);\n        },\n        /**\n         * Set line shape\n         * @param shape\n         */\n        setShape: function (shape) {\n            this.settings.icon = shape;\n        },\n        /**\n         * Set color\n         */\n        setColor: function (color) {\n            this.settings.color = color;\n        },\n        /**\n         * Set metadata for this line\n         * @method setMetadataId\n         * @param {String} id\n         */\n        setMetadataId: function (id) {\n            this.metadataId = id.toString();\n        },\n        /**\n         * Set start time code\n         * @method setTcin\n         * @param {Object} tcin\n         */\n        setTcin: function (tcin) {\n            if (this.isZoomable() === true || this.getHasElements() === false) {\n                this.tcin = parseFloat(tcin);\n            }\n            if (this.focusComponent !== null && this.focusLine !== null) {\n                this.focusComponent.setTc(this.tcin, this.tcout);\n                this.focusLine.setTcin(this.focusComponent.getFocusTcin());\n            }\n        },\n        /**\n         * Set end time code\n         * @method setTcout\n         * @param {Object} tcout\n         */\n        setTcout: function (tcout) {\n            if (this.isZoomable() === true || this.getHasElements() === false) {\n                this.tcout = parseFloat(tcout);\n            }\n            if (this.focusComponent !== null && this.focusLine !== null) {\n                this.focusComponent.setTc(this.tcin, this.tcout);\n                this.focusLine.setTcout(this.focusComponent.getFocusTcout());\n            }\n        },\n        /**\n         * Set zoom tc\n         * @method setZoomTc\n         * @param {Number} tcin\n         * @param {Number} tcout\n         */\n        setZoomTc: function (tcin, tcout) {\n            this.zTcin = parseFloat(tcin);\n            this.zTcout = parseFloat(tcout);\n            if (this.zoomableZone !== null) {\n                this.zoomableZone.setZoomTc(parseFloat(tcin), parseFloat(tcout));\n            }\n        },\n        /**\n         * Set zoom propertiy\n         * @method setZoomProperty\n         * @param {Object} zoomProperty\n         */\n        setZoomProperty: function (zoomProperty) {\n            this.zoomProperty = zoomProperty;\n        },\n        /**\n         * Set current time\n         * @method setCurrentTime\n         * @param {Number} tc time code\n         */\n        setCurrentTime: function (tc) {\n            this.currentTime = parseFloat(tc);\n            if (this.settings.timecursor === true) {\n                this.updateTimeCursor();\n            }\n        },\n        /**\n         * Return true if component has elements\n         * @method getHasElements\n         * @returns {Boolean}\n         */\n        getHasElements: function () {\n            return this.hasElements;\n        },\n        /**\n         * Return true if this component is focusable\n         * @method isFocusable\n         * @returns {Boolean}\n         */\n        isFocusable: function () {\n            return this.focusable;\n        },\n        /**\n         * Return true if this component is zoomable\n         * @method isZoomable\n         * @returns {Boolean}\n         */\n        isZoomable: function () {\n            return this.zoomable;\n        },\n        /**\n         * In charge to add items with sort by priority and tc\n         * @method addItems\n         * @param {Array} listOfdata description\n         */\n        addItems: function (listOfdata) {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \" addItems/ZoomLevel:\" + this.zoomLevel);\n                this.logger.warn(listOfdata);\n            }\n            var zoomProperty = this.zoomProperty;\n            this.hasElements = true;\n            if (listOfdata !== null) {\n                // sort byt tc level\n                listOfdata.sort(function (a, b) {\n\n                    if (a.hasOwnProperty(zoomProperty) && b.hasOwnProperty(zoomProperty)) {\n                        // convert to integers from strings\n                        a = parseInt(a[zoomProperty]);\n                        b = parseInt(b[zoomProperty]);\n                    }\n                    // compare\n                    if (a > b) {\n                        return 1;\n                    }\n                    else if (a < b) {\n                        return -1;\n                    }\n                    else {\n                        return 0;\n                    }\n                });\n                // sort by tc\n                listOfdata.sort(function (a, b) {\n                    // convert to integers from strings\n                    a = parseInt(a.tc);\n                    b = parseInt(b.tc);\n                    // compare\n                    if (a > b) {\n                        return 1;\n                    }\n                    else if (a < b) {\n                        return -1;\n                    }\n                    else {\n                        return 0;\n                    }\n                });\n                // Enabling zoom state\n                if (this.settings.zoomHierarchies === true) {\n                    listOfdata = this.getHierarchiesData(this.zoomLevel, listOfdata, this.settings.withParentLevel);\n                }\n\n                for (var i = 0;\n                     i < listOfdata.length;\n                     i++) {\n                    this.addItem(listOfdata[i]);\n                }\n                this.updateTooltip();\n\n            }\n            this.listOfdata = listOfdata;\n            if (this.settings.overlap === true) {\n                this.updateDisplayItems();\n            }\n            if (this.focusLine !== null) {\n                this.focusLine.removeItems();\n                this.focusLine.addItems(listOfdata);\n            }\n        },\n        /**\n         * In charge to clear selected items\n         * @returns {undefined}\n         */\n        clearSelectedItems: function () {\n            this.mediaPlayer.removeAllSelectedItems();\n            this.mainContainer.find('.line-content .item').removeClass('selected');\n        },\n        /**\n         * In charge to update display tool tip state.\n         * @method updateTooltip\n         */\n        updateTooltip: function () {\n            this.mainContainer.find('.line-content .item.cuepoint').tooltip(this.tooltipConfiguration);\n            this.mainContainer.find('.line-content .item.segment').tooltip(this.tooltipConfiguration);\n        },\n        /**\n         * In charge to update time cursor\n         * @method updateTimeCursor\n         */\n        updateTimeCursor: function () {\n            var tcin = parseFloat((this.zoomable === false) ? this.tcOffset : this.tcin);\n            var tcout = parseFloat((this.zoomable === false) ? this.duration : this.tcout);\n            this.mainContainer.find('.timecursor').css('left', ((this.currentTime - tcin) * 100) / (tcout - tcin) + '%');\n        },\n        /**\n         * Return data with zoom level\n         * @method getHierarchiesData\n         * @param {Object} zoomLevel\n         * @param {Object} listOfdata\n         * @param {Object} withParentLevel\n         * @returns {Array|Object|Array.getHierarchiesData.data}\n         */\n        getHierarchiesData: function (zoomLevel, listOfdata, withParentLevel) {\n            var zLevel = parseInt(zoomLevel.toString());\n            var data = [];\n            if (withParentLevel === true) {\n                do\n                {\n                    /* jshint loopfunc:true */\n                    data = $(listOfdata).filter(function (i) {\n                        return listOfdata[i].tclevel === zLevel;\n                    });\n                    if (data.length > 0) {\n                        return data;\n                    }\n\n                    zoomLevel--;\n                } while (zoomLevel > 0);\n            }\n            else {\n                data = $(listOfdata).filter(function (i) {\n                    return listOfdata[i].tclevel === zLevel;\n                });\n            }\n\n            return data;\n        },\n        /**\n         * Update the display state of the priority item condition.\n         * @method updateDisplayItems\n         */\n        updateDisplayItems: function () {\n            var listOfItems = this.mainContainer.find('.line-content').first().find('.item');\n            var lineContent = this.mainContainer.find('.line-content').first();\n            var item = null;\n            var lastItem = null;\n            var lastPos,\n                newPos,\n                itemWidth = null;\n            for (var i = listOfItems.length - 1;\n                 i >= 0;\n                 i--) {\n                item = $(listOfItems[i]);\n                if (i !== listOfItems.length - 1) {\n                    lastItem = $(lineContent).find('.item.on').first();\n                    if (lastItem.length > 0) {\n                        lastPos = lastItem.offset().left;\n                        newPos = item.offset().left;\n                        itemWidth = (item.hasClass('image') && !item.hasClass('segment')) ? item.find('img').width() : item.width();\n                        if (lastPos - newPos > itemWidth) {\n                            item.addClass('on');\n                            item.show();\n                        }\n                        else {\n                            item.addClass('off');\n                            item.hide();\n                        }\n                    }\n                }\n                else {\n                    item.addClass('on');\n                }\n            }\n        },\n        /**\n         * Return display state\n         * @method getDisplayState\n         * @returns {String}\n         */\n        getDisplayState: function () {\n            var displayStateName = '';\n            if (this.displayStateIdx < this.displayStateList.length) {\n\n                displayStateName = this.displayStateList[this.displayStateIdx];\n                this.displayStateIdx++;\n            }\n            else {\n                this.displayStateIdx = 0;\n                displayStateName = this.displayStateList[this.displayStateIdx];\n                this.displayStateIdx++;\n            }\n            // Save display state in local storage\n            this.localStorageManager.setItem(this.Class.LocalStorageTimeDisplayStateNamespace, displayStateName);\n            return displayStateName;\n        },\n        /**\n         * Change display state\n         * @method changeDisplayState\n         */\n        changeDisplayState: function () {\n            this.mainContainer.removeClass(this.displayStateList.toString().replace(/,/g, ' ')).addClass(this.getDisplayState());\n        },\n        /**\n         * Return next item with time code\n         * @method getNextItem\n         * @param {Number} currentTime\n         */\n        getNextItem: function (currentTime) {\n            var lineContent = this.mainContainer.find('.line-content').first();\n            var item = lineContent.find('.item').filter(function (index, element) {\n                return Math.round($(element).attr('data-tc')) > Math.round(currentTime);\n            }).first();\n            if (item.is('[data-tc]')) {\n                return parseFloat(item.attr('data-tc'));\n            }\n            else {\n                return null;\n            }\n        },\n        /**\n         * Return prev item with time code\n         * @method getPrevItem\n         * @param {Number} currentTime\n         */\n        getPrevItem: function (currentTime) {\n            var lineContent = this.mainContainer.find('.line-content').first();\n            var item = lineContent.find('.item').filter(function (index, element) {\n                return Math.round($(element).attr('data-tc')) < Math.round(currentTime);\n            }).last();\n            if (item.is('[data-tc]')) {\n                return parseFloat(item.attr('data-tc'));\n            }\n            else {\n                return null;\n            }\n        },\n        /**\n         * Set zoom level\n         * @method setZoomLevel\n         * @param {Number} level\n         */\n        setZoomLevel: function (level) {\n            this.zoomLevel = level;\n        },\n        /**\n         * Fired on click to expand button\n         * @method onClickExpandBtn\n         * @param {Object} event\n         */\n        onClickExpandBtn: function (event) {\n            try {\n                if (event.data.self.mainContainer.hasClass('xs')) {\n                    event.data.self.mainContainer.removeClass(event.data.self.displayStateList.toString().replace(/,/g, ' '));\n                    event.data.self.mainContainer.addClass('lg');\n                    event.data.self.mainContainer.find('.expand-btn.plugin-btn').removeClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_OFF).addClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_ON);\n                }\n                else {\n                    event.data.self.mainContainer.removeClass(event.data.self.displayStateList.toString().replace(/,/g, ' '));\n                    event.data.self.mainContainer.addClass('xs');\n                    event.data.self.mainContainer.find('.expand-btn.plugin-btn').removeClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_ON).addClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_OFF);\n\n                }\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \" onClickExpandBtn hasClass :--\" + event.data.self.mainContainer.hasClass('xs'));\n                }\n            }\n            catch (e) {\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"initialize\" + e.toString());\n                }\n            }\n        },\n        /**\n         * Fired on click to next button\n         * @method onClickNextNavContorl\n         * @param {Object} event\n         */\n        onClickNextNavContorl: function (event) {\n            event.data.self.mainContainer.trigger(event.data.self.Class.NAV_CLICK, {\n                type: 'next'\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickNextNavContorl\");\n            }\n        },\n        /**\n         * Fired on click to prev button\n         * @method onClickPrevNavContorl\n         * @param {Object} event\n         */\n        onClickPrevNavContorl: function (event) {\n            event.data.self.mainContainer.trigger(event.data.self.Class.NAV_CLICK, {\n                type: 'prev'\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickPrevNavContorl\");\n            }\n        },\n        /**\n         * Fired on click to focus button\n         * @method onClickFocusBtn\n         * @param {Object} event\n         */\n        onClickFocusBtn: function (event) {\n            if (event.data.self.focusComponent === null) {\n                event.data.self.createFocusComponent();\n            }\n            event.data.self.mainContainer.removeClass('focus-off').addClass('focus-on');\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickHierarchicalZoomBtn\");\n            }\n        },\n        /**\n         * Fired on focus change positon\n         * @method onFocusZoneChange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onFocusZoneChange: function (event, data) {\n            event.data.self.focusLine.removeItems();\n            event.data.self.focusLine.setTcin(data.focusTcin);\n            event.data.self.focusLine.setTcout(data.focusTcout);\n            event.data.self.focusLine.addItems(event.data.self.listOfdata);\n        },\n        /**\n         * Fired on click to close button\n         * @method onClickCloseBtn\n         * @param {Object} event\n         */\n        onClickCloseBtn: function (event) {\n            event.data.self.mainContainer.trigger(event.data.self.Class.CLOSE_EVENT);\n        },\n        /**\n         * Fired on close event\n         * @method onCloseEventRecever\n         */\n        onCloseEventRecever: function (event) {\n            event.data.self.mainContainer.removeClass('focus-on').addClass('focus-off');\n        },\n        /**\n         * Fired on time change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onPluginTimeChange: function (event, data) {\n            event.data.self.setCurrentTime(data.currentTime);\n        },\n        /**\n         * Fired on click to callback icon\n         * @param {Object} event\n         */\n        onClickToCallback: function (event) {\n            if (typeof event.data.self.settings.callback !== \"undefined\" && event.data.self.settings.callback !== \"\") {\n                try {\n                    /* jslint evil: true */\n                    eval(event.data.self.settings.callback + '(event.data.self.getMetadataId())');\n                }\n                catch (e) {\n                    if (event.data.self.logger !== null) {\n                        event.data.self.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickToCallback \" + event.data.self.settings.callback);\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/cuepoints-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle cue point component\n * @class CuepointsComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.timeline.BaseComponent\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.timeline.BaseComponent.extend(\"fr.ina.amalia.player.plugins.timeline.CuepointsComponent\", {\n        eventTypes: {\n            DATA_CHANGE: \"fr.ina.amalia.player.plugins.timeline.CuepointsComponent.eventTypes.DATA_CHANGE\"\n        },\n        ComponentClassCss: \"cuepoints-component\",\n        ComponentModuleClassCss: \"module-cuepoints\",\n        COMPONENT_NAME: 'cuepoint'\n    },\n    {\n        /**\n         * In charge to initialize cue point component\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.mainContainer.on('click', '.cuepoint', {\n                self: this\n            }, this.onClickAtCuepoint);\n            if (this.settings.editable === true && this.settings.selectable === true) {\n\n                this.mainContainer.find('.module-cuepoints').selectable({\n                    filter: '.item',\n                    stop: $.proxy(this.onSelectStop, this)\n                });\n\n            }\n        },\n        /**\n         * In charge to crate the cue point\n         * @param {Number} tc time code\n         * @param {Number} percentPos\n         * @param {String} title\n         * @param {Number} level\n         * @return {Object} element\n         */\n        createCuePointElement: function (tc, percentPos, title, level) {\n            var self = this;\n            var icon = (typeof this.settings.icon !== \"undefined\" && this.settings.icon !== \"\" ) ? this.settings.icon : 'circle';\n            var container = $('<i>', {\n                'class': 'item cuepoint ajs-icon ajs-icon-' + icon,\n                'style': 'left: ' + percentPos + '%; color:' + this.settings.color + ';',\n                'title': title,\n                'data-tc': tc,\n                'data-tclevel': level\n            });\n\n            if (this.settings.editable === true) {\n                // draggable\n                container.draggable({\n                    axis: \"x\",\n                    drag: function (event, ui) {\n\n                        var targetElement = $(event.target);\n                        var parentElement = targetElement.parent();\n                        var newLeft = Math.max(0, ui.position.left);\n                        ui.position.left = Math.min(parentElement.first().width(), newLeft);\n                        var dragTc = self.zTcin + parseFloat(((self.zTcout - self.zTcin) * newLeft) / self.mainContainer.first().width());\n                        if (event.shiftKey === true) {\n                            var dataOffsetTc = dragTc - targetElement.data('metadata').tc;\n                            container.attr('title', 'Offset seconds : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(dataOffsetTc, self.settings.framerate, 'seconds'));\n                        }\n                        else {\n                            container.attr('title', fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(dragTc, self.settings.framerate, self.settings.timeFormat));\n                        }\n\n                        self.updateTooltip();\n                    }\n                });\n                container.on(\"dragstop\", {\n                    self: this\n                }, this.onDragStop);\n                container.css(\"position\", \"absolute\");\n            }\n            return container;\n        },\n        /**\n         * In charge to add item\n         * @param {Object} data\n         */\n        addItem: function (data) {\n            if (data.hasOwnProperty('tc')) {\n                //Item tc\n                var tc = parseFloat(data.tc);\n                //Global tc\n                var gtc = this.tcout - this.tcin;\n                var percentPos = ((tc - this.tcin) * 100) / gtc;\n                var lineContent = this.mainContainer.find('.line-content').first();\n                var title = (data.hasOwnProperty('label') === true && data.label !== '' && data.label !== null) ? data.label : 'Tc: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc, this.settings.framerate, this.settings.timeFormat);\n                var itemContainer = null;\n                var selectedData = (data.hasOwnProperty('selected') && data.selected === true);\n                if (tc >= this.tcin && tc <= this.tcout) {\n                    itemContainer = this.createCuePointElement(tc, percentPos, title, data.tclevel);\n                    itemContainer.data('metadata', data);\n                    if (selectedData) {\n                        itemContainer.addClass('selected');\n                        if (data.hasOwnProperty('formCreated') && data.formCreated === false) {\n                            this.mainContainer.trigger(this.Class.CLICK_SELECT, {\n                                tc: tc,\n                                metadata: data\n                            });\n                        }\n                    }\n                    //set type\n                    if (data.hasOwnProperty('type') && data.type !== null) {\n                        itemContainer.attr('data-item-type', data.type);\n                    }\n                    lineContent.append(itemContainer);\n                    if (this.logger !== null) {\n                        this.logger.trace(this.Class.fullName, \"addItem tcin: \" + this.tcin + \" tcout: \" + this.tcout + \" tc:\" + tc + \" percentPos:\" + percentPos);\n                    }\n                }\n            }\n        },\n        /**\n         * In charge to remove items\n         */\n        removeItems: function () {\n            this.mainContainer.find('.line-content .cuepoint').remove();\n        },\n        /** events* */\n        /**\n         * Fired on click event at the cue point\n         * @method onClickAtSegment\n         * @param {Object} event\n         * @event fr.ina.amalia.player.components.CuepointsComponent.eventTypes.CLICK\n         */\n        onClickAtCuepoint: function (event) {\n            event.stopPropagation();\n            var currentTarget = $(event.currentTarget);\n            var tc = parseFloat(currentTarget.data('tc'));\n            var data = $(event.currentTarget).data('metadata');\n            // Alt+Click\n            if (event.ctrlKey && event.altKey && data.selected !== true && event.data.self.settings.editable === true) {\n                currentTarget.addClass('selected');\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_SELECT, {\n                    tc: tc,\n                    metadata: data\n                });\n            } else if (event.altKey && event.data.self.settings.editable === true) {\n                event.data.self.clearSelectedItems();\n                currentTarget.addClass('selected');\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_SELECT, {\n                    tc: tc,\n                    metadata: data\n                });\n            }\n            else {\n                event.data.self.clearSelectedItems();\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_TC, {\n                    tc: tc\n                });\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickAtCuepoint tc:\" + tc);\n            }\n        },\n        /**\n         * Fired on drag stop event\n         * @method onDragStop\n         * @param {Object} event\n         */\n        onDragStop: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tc = event.data.self.zTcin + parseFloat(((event.data.self.zTcout - event.data.self.zTcin) * currentTarget.position().left) / event.data.self.mainContainer.first().width());\n            if (event.shiftKey === true) {\n                var offsetTc = tc - currentTarget.data('metadata').tc;\n                event.data.self.localisationManager.shiftLocBlock(event.data.self.mediaPlayer.getMetadataById(event.data.self.getMetadataId()), offsetTc, event.data.self.mediaPlayer.getTcin(), event.data.self.mediaPlayer.getTcout(), event.altKey);\n            }\n            else {\n                currentTarget.data('metadata').tc = tc;\n                if (currentTarget.data('metadata').hasOwnProperty('tcin')) {\n                    currentTarget.data('metadata').tcin = tc;\n                }\n            }\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.DATA_CHANGE, {\n                id: event.data.self.getMetadataId()\n            });\n        },\n        /**\n         * Triggered at the end of the select operation.\n         */\n        onSelectStop: function () {\n            this.clearSelectedItems();\n            var self = this;\n            this.mainContainer.find('.module-cuepoints').find('.item.ui-selected').each(function (i, e) {\n                var element = $(e);\n                element.addClass('selected');\n                var metadata = element.data('metadata');\n                if (metadata !== null) {\n                    metadata.selected = true;\n                    self.mainContainer.trigger(self.Class.CLICK_SELECT, {\n                        tc: metadata.tc,\n                        metadata: metadata\n                    });\n                }\n            });\n            this.mainContainer.find('.module-cuepoints').selectable(\"option\", \"cancel\", \".item\");\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/default-configuration.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * Base class on all components used in timeline plugin\n * @class BaseComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @param {Object} settings\n * @param {Object} container\n */\n$.Class(\"fr.ina.amalia.player.plugins.timeline.DefaultConfiguration\", {\n    tooltipConfiguration: {\n        position: {\n            my: \"center bottom-20\",\n            at: \"center top\",\n            delay: 3000,\n            using: function (position, feedback) {\n                $(this).css(position);\n                $(\"<div>\").addClass(\"ajs-arrow\").addClass(feedback.vertical).addClass(feedback.horizontal).appendTo(this);\n            }\n        },\n        content: function () {\n            var element = $(this);\n            var title = element.attr('title');\n            if (element.is(\"[data-src]\")) {\n                var src = element.attr('data-src');\n                return \"<img class='image' alt='\" + title + \"' src='\" + src + \"' />\";\n            }\n            else {\n                title = title.replace(/(?:\\r\\n|\\r|\\n)/g, '<br />');\n                return \"<p>\" + title + \"</p>\";\n            }\n        }\n    }\n}, {});\n"
  },
  {
    "path": "src/plugins/timeline/focus-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle focus component\n * @class FocusComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.timeline.BaseComponent\n * @param {Object} settings\n * @param {Object} container\n */\n$.Class(\"fr.ina.amalia.player.plugins.components.FocusComponent\", {\n        CONTAINER_NAME: \"focus-container\",\n        eventTypes: {\n            FOCUS_ZONE_CHANGE: \"fr.ina.amalia.player.plugins.components.FocusComponent.FOCUS_ZONE_CHANGE\",\n            ZOOM_ZONE_CHANGE: \"fr.ina.amalia.player.plugins.components.FocusComponent.ZOOM_ZONE_CHANGE\"\n        }\n    },\n    {\n        /**\n         * Instance of logger\n         * @property logger\n         * @type {Object}\n         * @default null\n         */\n        logger: null,\n        /**\n         * Settings\n         * @property settings\n         * @type {Object}\n         * @default \"{}\"\n         */\n        settings: {},\n        /**\n         * Main container\n         * @property mainContainer\n         * @type {Object}\n         * @default null\n         */\n        mainContainer: null,\n        /**\n         * Container focus element\n         * @property container\n         * @type {Object}\n         * @default null\n         */\n        container: null,\n        /**\n         * True for mode focus\n         * @property modeFocus\n         * @type {Boolean}\n         * @default \"{}\"\n         */\n        modeFocus: null,\n        /**\n         * Start time code\n         * @property tcin\n         * @type {Number}\n         * @default 0\n         */\n        tcin: 0,\n        /**\n         * End time code\n         * @property tcout\n         * @type {Number}\n         * @default 0\n         */\n        tcout: 0,\n        /**\n         * Media duration\n         * @property duration\n         * @type {Object}\n         * @default \"{}\"\n         */\n        duration: 0,\n        /**\n         * Tc Offset\n         * @property tcOffset\n         * @type {Object}\n         * @default \"{}\"\n         */\n        tcOffset: 0,\n        /**\n         * In charge to init this component\n         * @constructor\n         * @param {Object} container\n         * @param {Number} duration\n         * @param {Object} settings\n         */\n        init: function (container, duration, settings) {\n            this.settings = $.extend({\n                debug: false,\n                modeFocus: true,\n                tcOffset: 0\n            }, settings || {});\n            this.modeFocus = this.settings.modeFocus;\n            this.tcOffset = parseFloat(this.settings.tcOffset);\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.mainContainer = container;\n            this.duration = parseFloat(duration);\n            if (this.mainContainer.length === 0 || typeof this.mainContainer !== \"object\") {\n                throw new Error(\"Your container is empty.\");\n            }\n            this.initialize();\n        },\n        /**\n         * In charge to initialize this component\n         */\n        initialize: function () {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"initialize\");\n            }\n            this.createContainer();\n            this.setMode();\n            this.mainContainer.on(fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.eventTypes.RANGE_CHANGE, {\n                self: this\n            }, this.onTimeRangeChange);\n        },\n        /**\n         * In charge to create focus component\n         * @method createContainer\n         */\n        createContainer: function () {\n            this.container = $('<div>', {\n                'class': this.Class.CONTAINER_NAME\n            });\n            // resizable\n            this.container.resizable({\n                handles: 'w,e',\n                // ghost : true,\n                create: function (event) {\n                    $(event.target).find('div.ui-resizable-handle').addClass('ajs-icon ajs-icon-reorder');\n                },\n                start: function (event) {\n                    var parentElement = $(event.target).parent();\n                    var maxWidth = parentElement.width();\n                    $(event.target).resizable(\"option\", \"minWidth\", 40);\n                    // Limit la largeur max\n                    $(event.target).resizable(\"option\", \"maxWidth\", maxWidth);\n                }\n            });\n            this.container.on(\"resizestop\", {\n                self: this\n            }, this.onResizeStop);\n            // draggable\n            this.container.draggable({\n                axis: \"x\",\n                drag: function (event, ui) {\n                    var targetElement = $(event.target);\n                    var parentElement = targetElement.parent();\n                    var newLeft = Math.max(0, ui.position.left);\n                    ui.position.left = Math.min(parentElement.first().width() - targetElement.width(), newLeft);\n                }\n            }).css(\"position\", \"absolute\");\n            this.container.on(\"dragstop\", {\n                self: this\n            }, this.onDragStop);\n            this.mainContainer.append(this.container);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"createContainer\");\n            }\n        },\n        /**\n         * In charge to set component mode\n         * @method createContainer\n         */\n        setMode: function () {\n            if (this.modeFocus === true) {\n                this.mainContainer.find('.' + this.Class.CONTAINER_NAME).addClass('focus');\n            }\n            else {\n                this.mainContainer.find('.' + this.Class.CONTAINER_NAME).addClass('zoom');\n            }\n        },\n        /**\n         * Return the main container\n         * @method getContainer\n         */\n        getContainer: function () {\n            return this.mainContainer;\n        },\n        /**\n         * Set tcin and tcout\n         * @method setTc\n         */\n        setTc: function (tcin, tcout) {\n            this.tcin = parseFloat(tcin);\n            this.tcout = parseFloat(tcout);\n            this.mainContainer.find('.' + this.Class.CONTAINER_NAME).attr('tcin', parseFloat(tcin));\n            this.mainContainer.find('.' + this.Class.CONTAINER_NAME).attr('tcout', parseFloat(tcout));\n\n            this.selectedZoneChange();\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"setTc: default tc:\" + this.tcin + \":\" + this.tcout);\n            }\n        },\n        /**\n         * Set zoom time code\n         * @method setZoomTc\n         * @param zTcin\n         * @param zTcout\n         */\n        setZoomTc: function (zTcin, zTcout) {\n            zTcin = zTcin - this.settings.tcOffset;\n            zTcout = zTcout - this.settings.tcOffset;\n            var duration = this.duration - this.settings.tcOffset;\n            this.resize(((zTcout - zTcin) * 100) / duration, (zTcin * 100) / duration);\n        },\n        /**\n         * Update container size\n         * @param {Number} percentWidth\n         * @param {Number} percentOffset\n         */\n        resize: function (percentWidth, percentOffset) {\n            this.mainContainer.find('.' + this.Class.CONTAINER_NAME).first().css({\n                'width': Math.min(Math.max(0, parseFloat(percentWidth)), 100) + '%',\n                'left': Math.min(Math.max(0, parseFloat(percentOffset)), 100) + '%'\n            });\n            this.container.trigger(\"dragstop\");\n        },\n        /**\n         * trigger select zone change\n         * @method selectedZoneChange\n         * @returns {Number}\n         */\n        selectedZoneChange: function () {\n            var focusTcin = this.getFocusTcin();\n            var focusTcout = this.getFocusTcout();\n            // Trigger\n            if (this.modeFocus === true) {\n                // Trigger Focus event\n                this.mainContainer.trigger(this.Class.eventTypes.FOCUS_ZONE_CHANGE, {\n                    focusTcin: focusTcin,\n                    focusTcout: focusTcout\n                });\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"selectedZoneChange trigger event : \" + this.Class.eventTypes.FOCUS_ZONE_CHANGE + \" focus focusTcin:\" + focusTcin + \" focusTcout:\" + focusTcout);\n                }\n            }\n            else {\n                // Trigger zoom event\n                this.mainContainer.trigger(this.Class.eventTypes.ZOOM_ZONE_CHANGE, {\n                    zTcin: focusTcin,\n                    zTcout: focusTcout\n                });\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"selectedZoneChange trigger event : \" + this.Class.eventTypes.ZOOM_ZONE_CHANGE + \" focus zTcin:\" + focusTcin + \" zTcout:\" + focusTcout);\n                }\n            }\n\n        },\n        /**\n         * Return focus start time code\n         * @method getFocusTcin\n         * @returns {Number}\n         */\n        getFocusTcin: function () {\n            var selectedTcin = parseFloat(((this.tcout - this.settings.tcOffset - this.tcin) * this.mainContainer.find(\".\" + this.Class.CONTAINER_NAME).first().position().left) / this.mainContainer.first().width());\n            return Math.max(0, this.tcin + selectedTcin);\n        },\n        /**\n         * Return focus end time code\n         * @returns {Number}\n         */\n        getFocusTcout: function () {\n            var focusTcin = this.getFocusTcin();\n            var selectedTcout = parseFloat(((this.tcout - this.tcOffset - this.tcin) * (this.mainContainer.find(\".\" + this.Class.CONTAINER_NAME).first().width())) / this.mainContainer.first().width());\n            return Math.min(this.duration, focusTcin + selectedTcout);\n        },\n        /**\n         * Fired on time range change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onTimeRangeChange: function (event, data) {\n            event.data.self.setTc(data.tcin, data.tcout);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onTimeRangeChange default tc:\" + data.tcin + \":\" + data.tcout);\n            }\n        },\n        /**\n         * Fired on drag stop event\n         * @param {Object} event\n         */\n        onDragStop: function (event) {\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onDragStop\");\n            }\n            event.data.self.selectedZoneChange();\n        },\n        /**\n         * Fired on resize event\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onResizeStop: function (event, ui) {\n            var element = $(ui.element);\n            if (event.data.self.modeFocus === true) {\n                element.css('height', '50%');\n            }\n            else {\n                element.css('height', '100%');\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onResizeStop\");\n            }\n            event.data.self.selectedZoneChange();\n        }\n\n    });\n"
  },
  {
    "path": "src/plugins/timeline/histogram-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle histogram component\n * @class HistogramComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.timeline.BaseComponent\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.timeline.BaseComponent.extend(\"fr.ina.amalia.player.plugins.timeline.HistogramComponent\", {\n        ComponentClassCss: \"histogram-component\",\n        ComponentModuleClassCss: \"module-histogram\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Creates a canvas object on which to draw.\n         * @property canvas\n         * @type Object\n         * @default \"null\"\n         */\n        canvas: null,\n        /**\n         * If true, histogram was created.\n         * @property histogramIsCreated\n         * @type Boolean\n         * @default False\n         */\n        histogramIsCreated: false,\n        initialize: function () {\n            this._super();\n            this.settings = $.extend({\n                mirror: false,\n                color: '#ffffff'\n                // in charge to duplicate hitogram line\n            }, this.settings || {});\n            this.histogramIsCreated = false;\n            this.canvas = new Raphael(this.mainContainer.find('.line-content').get(0));\n            this.mainContainer.on('click', {\n                self: this\n            }, this.onClickAtHistogram);\n        },\n        /**\n         * In charge to create a histogram into the composant container.\n         * @param {String} posbins string data encode in base64\n         * @param {Number} posmax max positive values\n         * @param {String} negbins string data encode in base64\n         * @param {Number} negmax max negatif values\n         * @param {Number} nbbins nombre de valeurs\n         */\n        createHistogram: function (posbins, posmax, negbins, negmax, nbbins) {\n            var positiveValues = fr.ina.amalia.player.helpers.UtilitiesHelper.base64DecToArr(posbins, nbbins);\n            var negativeValues = null;\n            var maxHeight = parseInt(posmax);\n            if (negbins !== null && negmax !== null) {\n                negativeValues = fr.ina.amalia.player.helpers.UtilitiesHelper.base64DecToArr(negbins, nbbins);\n                maxHeight = parseInt(posmax + negmax);\n            }\n            this.canvas.setViewBox(0, 0, nbbins, maxHeight, false);\n            this.canvas.setSize('100%', '100%');\n            this.canvas.canvas.setAttribute('preserveAspectRatio', 'none');\n            var itemPositiveValue = null;\n            var itemNegativeValue = null;\n            var color = typeof this.settings.color === \"undefined\" ? \"#FFFFFF\" : this.settings.color;\n            for (var i = 0;\n                 i < nbbins;\n                 i++) {\n                itemPositiveValue = parseInt(positiveValues[i]);\n                this.canvas.path('M' + i + ' ' + posmax + 'l 0 -' + itemPositiveValue).attr({\n                    'type': 'path',\n                    'stroke': color,\n                    'stroke-width': '1',\n                    'fill': color\n                });\n                if (negativeValues !== null) {\n                    itemNegativeValue = parseInt(negativeValues[i]);\n                    this.canvas.path('M' + i + ' ' + negmax + 'l 0 ' + itemNegativeValue).attr({\n                        'type': 'path',\n                        'stroke': color,\n                        'stroke-width': '1',\n                        'fill': color\n                    });\n                }\n            }\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"CreateHistogramWithMirror posmax:\" + posmax + \"  negmax:\" + negmax + \"/\" + \" nbbins:\" + nbbins);\n            }\n        },\n        /**\n         * In charge to create a histogram with mirror to container.\n         * @param {String} posbins string data encode in base64\n         * @param {Number} posmax max positive values\n         * @param {Number} nbbins\n         */\n        createHistogramWithMirror: function (posbins, posmax, nbbins) {\n            var positiveValues = fr.ina.amalia.player.helpers.UtilitiesHelper.base64DecToArr(posbins, nbbins);\n            this.canvas.setViewBox(0, 0, nbbins, posmax * 2, false);\n            this.canvas.setSize('100%', '100%');\n            this.canvas.canvas.setAttribute('preserveAspectRatio', 'none');\n            var itemPositiveValue = null;\n            for (var i = 0;\n                 i < nbbins;\n                 i++) {\n                itemPositiveValue = parseInt(positiveValues[i]);\n                this.canvas.path('M' + i + ' ' + posmax + 'l 0 -' + itemPositiveValue).attr({\n                    'type': 'path',\n                    'stroke': this.settings.color,\n                    'stroke-width': '1',\n                    'fill': this.settings.color\n                });\n                this.canvas.path('M' + i + ' ' + posmax + 'l 0 ' + itemPositiveValue).attr({\n                    'type': 'path',\n                    'stroke': this.settings.color,\n                    'stroke-width': '1',\n                    'fill': this.settings.color\n                });\n            }\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"CreateHistogramWithMirror posmax:\" + posmax + \"/\" + \" nbbins:\" + nbbins);\n            }\n        },\n        /**\n         * Set Zoom Tc\n         * @param {Number} tcin\n         * @param {Number} tcout\n         */\n        setZoomTc: function (tcin, tcout) {\n            this._super(tcin, tcout);\n            if (this.settings.zoomable === true) {\n                var _tcin = parseInt(this.settings.tcOffset);\n                var _tcout = parseInt(this.settings.duration);\n                var dt = _tcout - _tcin;\n                var d = this.zTcout - this.zTcin;\n                var pW = (100 * dt) / d;\n                var left = (pW * (this.zTcin - parseInt(this.settings.tcOffset))) / dt;\n                this.mainContainer.find('.line-content').css({\n                    'left': -left + '%',\n                    'width': pW + '%'\n                });\n            }\n        },\n        /**\n         * In charge to add histogram\n         * @param {Object} data\n         */\n        addItem: function (data) {\n            if (this.histogramIsCreated === false && data.data !== null && data.data.hasOwnProperty('histogram') === true && data.data.histogram !== null && data.data.histogram.length > 0) {\n                this.canvas.clear();\n                for (var i = 0;\n                     i < data.data.histogram.length;\n                     i++) {\n                    var item = data.data.histogram[i];\n                    if (item.hasOwnProperty('posbins') && item.hasOwnProperty('posmax') && item.hasOwnProperty('nbbins')) {\n                        try {\n                            if (this.settings.mirror === true) {\n                                this.createHistogramWithMirror(item.posbins, item.posmax, item.nbbins);\n                            }\n                            else {\n                                this.createHistogram(item.posbins, item.posmax, item.hasOwnProperty('negbins') ? item.negbins : null, item.hasOwnProperty('negmax') ? item.negmax : null, item.nbbins);\n                            }\n                        }\n                        catch (error) {\n                            if (this.logger !== null) {\n                                this.logger.warn(\"Error to create histogram\");\n                                this.logger.warn(error.stack);\n                            }\n                        }\n                    }\n                    else if (this.logger !== null) {\n                        this.logger.warn(this.Class.fullName + \"Error to create histogram\");\n                        this.logger.warn(item);\n                    }\n                }\n                this.histogramIsCreated = true;\n            }\n        },\n        /**\n         * In charge to clear elements\n         */\n        removeItems: function () {\n            // There elements are never deleted.\n        },\n        /**\n         * In  charge to set histogram state\n         * @param state\n         */\n        setHistogramIsCreated: function (state) {\n            this.histogramIsCreated = state;\n        },\n        /**\n         * Fired on click event\n         * @method onClickAtHistogram\n         * @param {Object} event\n         */\n        onClickAtHistogram: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var posX = ((event.clientX - currentTarget.offset().left) + currentTarget.position().left) * 100 / event.data.self.mainContainer.width();\n            var tc = null;\n            if (event.data.self.zoomable === false) {\n                tc = ((event.data.self.tcout - event.data.self.tcin) * posX / 100) + parseInt(event.data.self.settings.tcOffset);\n\n            }\n            else {\n                tc = (((event.data.self.zTcout - event.data.self.zTcin) * posX / 100) + event.data.self.zTcin);\n            }\n            event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_TC, {\n                tc: tc\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickAtHistogram\");\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/images-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle image component\n * @class ImagesComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.timeline.BaseComponent\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.timeline.BaseComponent.extend(\"fr.ina.amalia.player.plugins.timeline.ImagesComponent\", {\n        ComponentClassCss: \"images-component\",\n        ComponentModuleClassCss: \"module-images\",\n        COMPONENT_NAME: 'image'\n    },\n    {\n        /**\n         * Tool tip configuration\n         * @property timeZoomComponent\n         * @type {Object}\n         * @default null\n         */\n        tooltipConfiguration: {\n            position: {\n                my: \"center bottom-20\",\n                at: \"center top\",\n                delay: 3000,\n                using: function (position, feedback) {\n                    $(this).css(position);\n                    $(\"<div>\").addClass(\"ajs-arrow\").addClass(\"timeline-images-component\").addClass(feedback.vertical).addClass(feedback.horizontal).appendTo(this);\n                }\n            },\n            content: function () {\n                var element = $(this);\n                var title = element.attr('title');\n                if (element.is(\"[data-src]\")) {\n                    var src = element.attr('data-src');\n                    return \"<img class='timeline-images-component tooltip-image' alt='\" + title + \"' src='\" + src + \"' />\";\n                }\n                else {\n                    title = title.replace(/(?:\\r\\n|\\r|\\n)/g, '<br />');\n                    return \"<p>\" + title + \"</p>\";\n                }\n\n            }\n        },\n        initialize: function () {\n            this._super();\n            this.mainContainer.on('click', '.image', {\n                    self: this\n                },\n                this.onClickAtImage);\n        },\n        /**\n         * In charge to create image element\n         * @method createImageElement\n         * @param {Number} tcin timecode\n         * @param {Number} tcout\n         * @param {Number} percentWidth\n         * @param {Number} width\n         * @param {String} title\n         * @param {String} image\n         * @param {Number} level\n         * @return {Object} dom\n         */\n        createImageElement: function (tcin, tcout, percentWidth, width, title, image, level) {\n            var imageContainer = $('<img>', {\n                src: image,\n                class: 'content',\n                title: title,\n                'data-src': image\n            }).tooltip(this.tooltipConfiguration);\n            var imageElement = null;\n            if (tcout !== null) {\n                var flowline = $('<hr>', {\n                    class: 'flow',\n                    style: 'border-color:' + this.settings.color + '; color:' + this.settings.color + ';'\n                });\n                imageElement = $('<div>', {\n                    class: 'item image segment',\n                    style: 'left: ' + percentWidth + '%; width:' + width + '%;',\n                    title: title,\n                    'data-tc': tcin,\n                    'data-tclevel': level,\n                    'data-tcin': tcin,\n                    'data-tcout': tcout\n                }).append(flowline);\n\n                if (width > parseInt(this.settings.pointTogglePercentHeight)) {\n                    imageElement.append(imageContainer);\n                }\n                else {\n                    imageElement.tooltip(this.tooltipConfiguration);\n                }\n            }\n            else {\n                imageElement = $('<div>', {\n                    class: 'item image',\n                    style: 'left: ' + percentWidth + '%; margin-left:-' + this.settings.offset + 'px;',\n                    title: title,\n                    'data-tc': tcin,\n                    'data-tclevel': level\n                }).append(imageContainer);\n            }\n            // add image\n            imageElement.on(\"mouseenter mouseleave\", function (e) {\n                if (e.type === \"mouseenter\") {\n                    $(e.currentTarget).parent().find('.image').css('opacity', '.3');\n                    $(e.currentTarget).addClass('on').css('opacity', '1');\n                }\n                else {\n                    $(e.currentTarget).parent().find('.image').css('opacity', '1');\n                    $(e.currentTarget).removeClass('on').css('opacity', '1');\n                }\n            });\n            return imageElement;\n        },\n        /**\n         * In charge to add element\n         * @method getThumbPath\n         */\n        getThumbPath: function (data) {\n            var thumb = \"\";\n            if (data.hasOwnProperty('thumb') === true && data.thumb !== null && data.thumb !== '') {\n                thumb = data.thumb;\n                if (thumb.search('/^data:/') === -1) {\n                    thumb = this.settings.thumbDirectory + thumb;\n                }\n            }\n            return thumb;\n        },\n        /**\n         * In charge to add element\n         * @param {Object} data\n         */\n        addItem: function (data) {\n            var percentWidth = null;\n            var title = null;\n            var lineContent = null;\n            // global tc\n            var gtc = this.tcout - this.tcin;\n            var thumb = this.getThumbPath(data);\n            if (data.hasOwnProperty('tcin') && data.hasOwnProperty('tcout')) {\n                var itemTcin = parseFloat(data.tcin);\n                var itemTcout = parseFloat(data.tcout);\n                var width = ((itemTcout - itemTcin) * 100) / gtc;\n                percentWidth = ((itemTcin - this.tcin) * 100) / gtc;\n\n                // if data has label\n                if (data.hasOwnProperty('label') === true && data.label !== '' && data.label !== null) {\n                    title = data.label;\n                }\n                else {\n                    title = 'Tc in: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(itemTcin, this.settings.framerate, this.settings.timeFormat) + '\\n Tc out: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(itemTcout, this.settings.framerate, this.settings.timeFormat);\n                }\n\n                lineContent = this.mainContainer.find('.line-content').first();\n                if (itemTcin >= this.tcin && itemTcin <= this.tcout) {\n                    lineContent.append(this.createImageElement(itemTcin, itemTcout, percentWidth, width, title, thumb, data.tclevel));\n                    if (this.logger !== null) {\n                        this.logger.trace(this.Class.fullName, \"addItem tcin: \" + this.tcin + \" tcout: \" + this.tcout + \" itemTcin:\" + itemTcin + \" itemTcout:\" + itemTcout + \" percentWidth:\" + percentWidth);\n                    }\n                }\n            }\n            else if (data.hasOwnProperty('tc')) {\n                var tc = parseFloat(data.tc);\n                percentWidth = ((tc - this.tcin) * 100) / gtc;\n                title = (data.hasOwnProperty('label') === true && data.label !== '' && data.label !== null) ? data.label : fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc, this.settings.framerate, this.settings.timeFormat);\n                lineContent = this.mainContainer.find('.line-content').first();\n\n                if (tc >= this.tcin && tc <= this.tcout) {\n                    lineContent.append(this.createImageElement(tc, null, percentWidth, null, title, thumb, data.tclevel));\n                    if (this.logger !== null) {\n                        this.logger.trace(this.Class.fullName, \"addItem tc: \" + tc);\n                    }\n                }\n            }\n        },\n        /**\n         * In charge to remover all elements\n         */\n        removeItems: function () {\n            this.mainContainer.find('.line-content').first().find('.image').remove();\n        },\n        /**\n         * Fired on click event\n         * @method onClickAtImage\n         * @param {Object} event\n         * @event fr.ina.amalia.player.components.ImagesComponent.eventTypes.CLICK\n         */\n        onClickAtImage: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tcin = parseFloat(currentTarget.data('tc'));\n            event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_TC, {\n                tc: tcin\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickAtImage tcin:\" + tcin);\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/segments-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle segment component\n * @class SegmentsComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.timeline.BaseComponent\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.timeline.BaseComponent(\"fr.ina.amalia.player.plugins.timeline.SegmentsComponent\", {\n        ComponentClassCss: \"segments-component\",\n        ComponentModuleClassCss: \"module-segments\",\n        COMPONENT_NAME: 'segment',\n        eventTypes: {\n            DATA_CHANGE: \"fr.ina.amalia.player.plugins.timeline.SegmentsComponent.eventTypes.DATA_CHANGE\"\n        }\n    },\n    {\n        initialize: function () {\n            this._super();\n            this.mainContainer.on('dblclick ', '.segment', {\n                self: this\n            }, this.onDBLClickAtSegment);\n            this.mainContainer.on('click', '.segment', {\n                self: this\n            }, this.onClickAtSegment);\n            if (this.settings.editable === true && this.settings.selectable === true) {\n                this.mainContainer.find('.module-segments').selectable({\n                    filter: '.item',\n                    stop: $.proxy(this.onSelectStop, this)\n                });\n            }\n        },\n        /**\n         * In charge to create segment element\n         * @method createSegmentElement\n         * @param {Number} tcin time code\n         * @param {Number} tcout\n         * @param {Number} percentWidth\n         * @param {Number} width\n         * @param {String} title\n         * @return {Object} Dom\n         */\n        createSegmentElement: function (tcin, tcout, percentWidth, width, title) {\n            var self = this;\n            var color = (this.settings.color !== \"\") ? 'color:' + this.settings.color + '; background-color:' + this.settings.color + ';' : '';\n            var styleClass = (this.settings.marker === true) ? 'item segment marker' : 'item segment';\n            var container = $('<div>', {\n                class: styleClass,\n                style: 'left: ' + percentWidth + '%; width:' + width + '%;' + color,\n                title: title,\n                'data-tc': tcin,\n                'data-tcin': tcin,\n                'data-tcout': tcout\n            });\n            // Le composant sera dragable pour le mode édition\n            if (this.settings.editable === true) {\n                // resizable\n                container.resizable({\n                    handles: 'e,w',\n                    ghost: true,\n                    helper: \"ui-resizable-helper\",\n                    start: function (event, ui) {\n                        var parentElement = $(event.target).parent();\n                        var maxWidth = parentElement.width() - ui.element.position().left;\n                        // Limit la largeur max\n                        $(event.target).resizable(\"option\", \"maxWidth\", maxWidth);\n                        var targetOriginalEventElement = $(event.originalEvent.target);\n                        $(ui.element).data('resizeTcin', targetOriginalEventElement.hasClass('ui-resizable-w'));\n                        $(ui.element).data('resizeTcout', targetOriginalEventElement.hasClass('ui-resizable-e'));\n                    },\n                    resize: function (event, ui) {\n                        var resizeTcin = parseFloat(((self.zTcout - self.zTcin) * Math.max(0,ui.position.left)) / self.mainContainer.first().width()) + self.zTcin;\n                        var resizeTcout = tcin + parseFloat(((self.zTcout - self.zTcin) * ui.size.width) / self.mainContainer.first().width());\n                        container.attr('title', 'Tc in : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(resizeTcin, self.settings.framerate, self.settings.timeFormat) + '\\n Tc out : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(resizeTcout, self.settings.framerate, self.settings.timeFormat));\n                        self.updateTooltip();\n                    }\n\n                });\n                container.on(\"resizestop\", {\n                    self: this\n                }, this.onResizeStop);\n                // draggable\n                container.draggable({\n                    axis: \"x\",\n                    drag: function (event, ui) {\n                        var targetElement = $(event.target);\n                        var parentElement = targetElement.parent();\n                        var newLeft = Math.max(0, ui.position.left);\n                        ui.position.left = Math.min(parentElement.first().width() - targetElement.width(), newLeft);\n                        var dragTcin = parseFloat(((self.zTcout - self.zTcin) * ui.position.left) / self.mainContainer.first().width()) + self.zTcin;\n                        var dragTcout = parseFloat(((self.zTcout - self.zTcin) * targetElement.width()) / self.mainContainer.first().width()) + dragTcin;\n\n                        if (event.shiftKey === true) {\n                            var dataOffsetTc = dragTcin - targetElement.data('metadata').tcin;\n                            container.attr('title', 'Offset seconds : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(dataOffsetTc, self.settings.framerate, 'seconds'));\n                        }\n                        else {\n                            container.attr('title', 'Tc in : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(dragTcin, self.settings.framerate, self.settings.timeFormat) + '\\n Tc out : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(dragTcout, self.settings.framerate, self.settings.timeFormat));\n                        }\n                        self.updateTooltip();\n                    }\n                });\n                container.on(\"dragstop\", {\n                    self: this\n                }, this.onDragStop);\n                container.css(\"position\", \"absolute\");\n            }\n            return container;\n        },\n        /**\n         * In charge to add item\n         * @method addItem\n         * @param {Object} data\n         */\n        addItem: function (data) {\n            if (data.hasOwnProperty('tcin') && data.hasOwnProperty('tcout')) {\n                var tcin = parseFloat((this.zoomable === false) ? this.tcOffset : this.tcin);\n                var tcout = parseFloat((this.zoomable === false) ? this.duration : this.tcout);\n                var itemTcin = parseFloat(data.tcin);\n                var itemTcout = parseFloat(data.tcout);\n\n                var duration = tcout - tcin;\n                var width = ((itemTcout - itemTcin) * 100) / duration;\n                var percentWidth = ((itemTcin - tcin) * 100) / duration;\n                var title = null;\n                var selectedData = (data.hasOwnProperty('selected') && data.selected === true);\n                if ((data.hasOwnProperty('label') === true && data.label !== '' && data.label !== null)) {\n                    title = data.label;\n                }\n                else {\n                    title = 'Tc in : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(itemTcin, this.settings.framerate, this.settings.timeFormat) + '\\n Tc out : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(itemTcout, this.settings.framerate, this.settings.timeFormat);\n                }\n\n                var lineContent = this.mainContainer.find('.line-content').first();\n                var itemContainer = null;\n                if (itemTcin < tcout && itemTcout > tcin) {\n                    itemContainer = this.createSegmentElement(itemTcin, itemTcout, percentWidth, width, title);\n                    itemContainer.data('metadata', data);\n                    if (selectedData) {\n                        //Add style\n                        itemContainer.addClass('selected');\n                    }\n                    if (selectedData && data.hasOwnProperty('formCreated') && data.formCreated === false) {\n                        this.mainContainer.trigger(this.Class.CLICK_SELECT, {\n                            tc: itemTcin,\n                            metadata: data\n                        });\n                    }\n                    //set type\n                    if (data.hasOwnProperty('type') && data.type !== null) {\n                        itemContainer.attr('data-item-type', data.type);\n                    }\n                    lineContent.append(itemContainer);\n                    if (this.logger !== null) {\n                        this.logger.trace(this.Class.fullName, \"addItem tcin: \" + tcin + \" tcout: \" + tcout + \" itemTcin:\" + itemTcin + \" itemTcout:\" + itemTcout + \" percentWidth:\" + percentWidth);\n                    }\n                }\n            }\n        },\n        /**\n         * In charge to remove items\n         * @method removeItems\n         */\n        removeItems: function () {\n            var lineContent = this.mainContainer.find('.line-content').first();\n            lineContent.find('.segment').remove();\n        },\n        /**\n         * Fired on click event\n         * @method onClickAtSegment\n         * @param {Object} event\n         * @event fr.ina.amalia.player.components.SegmentsComponent.eventTypes.CLICK\n         */\n        onClickAtSegment: function (event) {\n            event.stopPropagation();\n            var currentTarget = $(event.currentTarget);\n            var tcin = parseFloat(currentTarget.data('tcin'));\n            var data = $(event.currentTarget).data('metadata');\n            // Alt+Click\n            if (event.altKey && event.data.self.settings.editable === true && typeof data === \"object\" && data.selected !== true) {\n                currentTarget.addClass('selected');\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_SELECT, {\n                    tc: tcin,\n                    metadata: data\n                });\n            }\n            else {\n                event.data.self.clearSelectedItems();\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_TC, {\n                    tc: tcin,\n                    metadata: data\n                });\n            }\n\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickAtSegment tcin:\" + tcin);\n            }\n        },\n        /**\n         * Fired on click event\n         * @method onClickAtSegment\n         * @param {Object} event\n         * @event fr.ina.amalia.player.components.SegmentsComponent.eventTypes.CLICK\n         */\n        onDBLClickAtSegment: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tcin = parseFloat(currentTarget.data('tcin'));\n            var data = $(event.currentTarget).data('metadata');\n            event.preventDefault();\n            if (event.data.self.settings.editable === true && typeof data === \"object\") {\n                event.data.self.clearSelectedItems();\n                if (data.hasOwnProperty(\"selected\") && data.selected === true) {\n                    currentTarget.removeClass('selected');\n                    event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_REMOVE_SELECT_ITEM, {\n                        tc: tcin,\n                        metadata: data\n                    });\n                }\n                else {\n                    currentTarget.addClass('selected');\n                    event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_SELECT, {\n                        tc: tcin,\n                        metadata: data\n                    });\n                }\n\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onDBLClickAtSegment tcin:\" + tcin);\n            }\n        },\n        /**\n         * Fired on drag stop event\n         * @method onDragStop\n         * @param {Object} event\n         */\n        onDragStop: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var zTcin = parseFloat((event.data.self.zoomable === false) ? event.data.self.tcOffset : event.data.self.zTcin);\n            var zTcout = parseFloat((event.data.self.zoomable === false) ? event.data.self.duration : event.data.self.zTcout);\n            var tcin = parseFloat(((zTcout - zTcin) * currentTarget.position().left) / event.data.self.mainContainer.first().width()) + zTcin;\n            var tcout = parseFloat(((zTcout - zTcin) * currentTarget.width()) / event.data.self.mainContainer.first().width()) + tcin;\n            if (event.shiftKey === true) {\n                var offsetTc = tcin - currentTarget.data('metadata').tcin;\n                event.data.self.localisationManager.shiftLocBlock(event.data.self.mediaPlayer.getMetadataById(event.data.self.getMetadataId()), offsetTc, event.data.self.mediaPlayer.getTcin(), event.data.self.mediaPlayer.getTcout(), event.altKey);\n            }\n            else {\n                currentTarget.data('metadata').tcin = tcin;\n                currentTarget.data('metadata').tcout = tcout;\n            }\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.DATA_CHANGE, {\n                id: event.data.self.getMetadataId()\n            });\n        },\n        /**\n         * Fired on resize stop event\n         * @method onResizeStop\n         * @param {Object} event\n         */\n        onResizeStop: function (event, ui) {\n            var currentTarget = $(event.currentTarget);\n            var zTcin = parseFloat((event.data.self.zoomable === false) ? event.data.self.tcOffset : event.data.self.zTcin);\n            var zTcout = parseFloat((event.data.self.zoomable === false) ? event.data.self.duration : event.data.self.zTcout);\n            var tcin = parseFloat(((zTcout - zTcin) * Math.max(0,currentTarget.position().left)) / event.data.self.mainContainer.first().width()) + zTcin;\n            var tcout = tcin + parseFloat(((zTcout - zTcin) * currentTarget.width()) / event.data.self.mainContainer.first().width());\n            // Fix for resize only one side\n            var element = $(ui.element);\n            if (element.data('resizeTcin') === true) {\n                $(event.currentTarget).data('metadata').tcin = tcin;\n            }\n            if (element.data('resizeTcout') === true) {\n                $(event.currentTarget).data('metadata').tcout = tcout;\n            }\n            event.data.self.updateTooltip();\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.DATA_CHANGE, {\n                id: event.data.self.getMetadataId()\n            });\n        },\n        /**\n         * Triggered at the end of the select operation.\n         */\n        onSelectStop: function () {\n            this.clearSelectedItems();\n            var self = this;\n            this.mainContainer.find('.module-segments').find('.item.ui-selected').each(function (i, e) {\n                var element = $(e);\n                element.addClass('selected');\n                var metadata = element.data('metadata');\n                if (metadata !== null) {\n                    metadata.selected = true;\n                    self.mainContainer.trigger(self.Class.CLICK_SELECT, {\n                        tc: metadata.tc,\n                        metadata: metadata\n                    });\n                }\n            });\n            this.mainContainer.find('.module-segments').selectable(\"option\", \"cancel\", \".item\");\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/tic-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to create all tic on the time axe\n * @class TicComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @param {Object} settings\n * @param {Object} container\n */\n$.Class(\"fr.ina.amalia.player.plugins.timeline.TicComponent\", {\n        eventTypes: {\n            CLICK: \"fr.ina.amalia.player.plugins.timeline.TicComponent.click\",\n            UPDATE_TC: \"fr.ina.amalia.player.plugins.timeline.TicComponent.updatetc\"\n        }\n    },\n    {\n        /**\n         * instance of the logger\n         * @property logger\n         * @type {Object}\n         * @default null\n         */\n        logger: null,\n        /**\n         * This component container\n         * @property settings\n         * @type {Object}\n         * @default \"{}\"\n         */\n        container: null,\n        /**\n         * Configuration\n         * @property settings\n         * @type {Object}\n         * @default \"{}\"\n         */\n        settings: {},\n        /**\n         * Start time code\n         * @property currentTcin\n         * @type {Number}\n         * @default 0\n         */\n        currentTcin: 0,\n        /**\n         * End time code\n         * @property currentTcout\n         * @type {Object}\n         * @default 0\n         */\n        currentTcout: 0,\n        /**\n         * Tool tip configuration\n         * @property tooltipConfiguration\n         * @type {Object}\n         * @default \"{}\"\n         */\n        tooltipConfiguration: {\n            position: {\n                my: \"center bottom-20\",\n                at: \"center top\",\n                delay: 3000,\n                using: function (position, feedback) {\n                    $(this).css(position);\n                    $(\"<div>\").addClass(\"ajs-arrow\").addClass(feedback.vertical).addClass(feedback.horizontal).appendTo(this);\n                }\n            },\n            content: function () {\n                var element = $(this);\n                var title = element.attr('title');\n                if (element.is(\"[data-src]\")) {\n                    var src = element.attr('data-src');\n                    return \"<img class='image' alt='\" + title + \"' src='\" + src + \"' />\";\n                }\n                else {\n                    title = title.replace(/(?:\\r\\n|\\r|\\n)/g, '<br />');\n                    return \"<p>\" + title + \"</p>\";\n                }\n            }\n        },\n        /**\n         * Tic preferred time code\n         * @property PREFERRED_TICS\n         * @type {Object}\n         * @default \"{}\"\n         */\n        PREFERRED_TICS: [\n            1,\n            5,\n            10,\n            100,\n            1000,\n            2000,\n            5000,\n            10000,\n            20000,\n            30000,\n            60000,\n            300000,\n            600000,\n            1800000\n        ],\n        /**\n         * Media duration\n         * @property tooltipConfiguration\n         * @type {Object}\n         * @default \"{}\"\n         */\n        mediaDuration: 0,\n        /**\n         * label width\n         * @property tcout\n         * @type {Object}\n         */\n        averageLabelSize: 50,\n        /**\n         * In charge to init this component\n         * @constuctor\n         * @param {Object} container\n         * @param {Object} settings\n         * @returns {undefined}\n         */\n        init: function (container, settings) {\n            this.settings = $.extend({\n                    debug: false\n                },\n                settings || {});\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.container = container;\n            this.mediaDuration = this.settings.duration * 1000;\n            this.averageLabelSize = 50;\n            this.initialize();\n        },\n        /**\n         * Initialize\n         * @method initialize\n         */\n        initialize: function () {\n\n            this.container.on('click', {\n                    self: this\n                },\n                this.onClick);\n            this.container.draggable({\n                axis: \"x\"\n            });\n            this.container.on('drag', {\n                    self: this\n                },\n                this.onDrag);\n            this.container.on('dragstop', {\n                    self: this\n                },\n                this.onDragStop);\n            // call function 200 ms after resize is complete.\n            $(window).on('resize', {\n                    self: this\n                },\n                this.onWindowResize);\n        },\n        /**\n         * Return tic with\n         * @returns {Number} px\n         */\n        getWidth: function () {\n            return Math.round(this.container.width());\n        },\n        /**\n         * Return preferred segments numbers\n         * @returns {Number}\n         */\n        getPreferredNbSegments: function () {\n            return Math.round(this.getWidth() / this.averageLabelSize);\n        },\n        /**\n         * In charge to create tic\n         * @param {Number} tc\n         * @param {Number} pos\n         * @param {Number} label\n         * @returns {Object} Dom\n         */\n        createTic: function (tc, pos, label) {\n            var element = $('<div>', {\n                'class': 'time-grid',\n                'data-tc': tc\n            });\n            var labelElement = $('<span>', {\n                'class': 'time',\n                'text': label,\n                'data-tc': tc,\n                'title': fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(tc / 1000, this.settings.framerate, this.settings.timeFormat)\n            });\n            element.append(labelElement);\n            element.css('left', pos + \"%\");\n            return element;\n        },\n        /**\n         * In charge to update segments\n         * @param {Number} tcin en ms\n         * @param {Number} tcout en ms\n         */\n        updateSegments: function (tcin, tcout) {\n            this.currentTcin = tcin;\n            this.currentTcout = tcout;\n            var duration = tcout - tcin;\n            var preferredNbSegments = this.getPreferredNbSegments();\n            var ticLength = 0;\n            var curNbTics = 0;\n            var curTicTc = tcin;\n            var t = -1;\n            do\n            {\n                t++;\n                curNbTics = Math.round(duration / this.PREFERRED_TICS[t]);\n            } while ((curNbTics > preferredNbSegments) && (t < (this.PREFERRED_TICS.length - 1)));\n            ticLength = this.PREFERRED_TICS[t];\n\n            curTicTc = Math.max(-1, (Math.ceil(tcin / ticLength) - curNbTics)) * ticLength;\n\n            tcout = Math.min(tcout + duration, this.mediaDuration);\n            var pos = 0;\n            // clear all labels\n            this.container.empty();\n\n            this.container.attr('data-duration', duration);\n            do\n            {\n                curTicTc += ticLength;\n                pos = (curTicTc - tcin) * 100 / duration;\n                this.container.append(this.createTic(curTicTc, pos, this.getFormatTc(curTicTc, ticLength)));\n            } while (curTicTc < tcout);\n            this.container.find('span.time').tooltip(this.tooltipConfiguration);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"updateSegments getWidth() : \" + this.getWidth() + \" preferredNbSegments :\" + preferredNbSegments);\n            }\n        },\n        /**\n         * Return time code with preferred time format\n         * @param {Number} tc ms\n         * @param {Number} interval ms\n         */\n        getFormatTc: function (tc, interval) {\n            tc = tc / 1000;\n            interval = interval / 1000;\n            var formatString = \"\";\n            var minutes = Math.floor(tc / 60);\n            var hours = Math.floor(minutes / 60);\n            var seconds = Math.floor(tc % 60);\n            var milliseconds = tc % 60;\n            minutes = Math.floor(minutes % 60);\n            minutes = (minutes >= 10) ? minutes : '0' + minutes;\n            hours = (hours >= 10) ? hours : '0' + hours;\n            seconds = (seconds >= 10) ? seconds : '0' + seconds;\n            if (interval > 3600) {\n                if (parseInt(hours) === 0) {\n                    formatString = hours + '::' + minutes + ':' + seconds + ' m';\n                }\n                else {\n                    formatString = hours + 'h';\n                }\n            }\n            else if (interval <= 3600 && interval >= 60) {\n                if (parseInt(hours) === 0) {\n                    formatString = minutes + ':' + seconds + ' m';\n                }\n                else {\n                    formatString = hours + ':' + minutes + ' h';\n                }\n            }\n            else if (interval < 60 && interval > 1) {\n                if (parseInt(hours) !== 0 && parseInt(minutes) === 0) {\n                    formatString = milliseconds.toFixed(2).toString() + ' s';\n                }\n                else {\n                    formatString = minutes + ':' + seconds + ' m';\n                }\n            }\n            else if (interval <= 1) {\n                if (interval <= 0.001) {\n                    formatString = milliseconds.toFixed(4).toString().split('.')[1] + ' ms';\n                }\n                else {\n                    formatString = seconds + \".\" + milliseconds.toFixed(2).toString().split('.')[1] + ' s';\n                }\n            }\n\n            return formatString;\n        },\n        /**\n         * Fired on click at time code\n         * @param {Object} event\n         */\n        onClick: function (event) {\n            var target = $(event.target);\n            event.preventDefault();\n            if (target.hasClass('time')) {\n                var tc = parseFloat(target.attr('data-tc'));\n                if (typeof tc === \"number\") {\n                    event.data.self.container.trigger(event.data.self.Class.eventTypes.CLICK, {\n                        tc: tc / 1000\n                    });\n                    if (event.data.self.logger !== null) {\n                        event.data.self.logger.trace(event.data.self.Class.fullName, \"onClick tc:\" + tc);\n                    }\n                }\n            }\n        },\n        /**\n         * Fired on drag event\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onDrag: function (event, ui) {\n            var targetElement = $(event.target);\n            var firstElementPos = Math.abs(parseInt(targetElement.find('.time-grid').first().position().left)); // Valeur\n            // absolue\n            var lastElementPos = parseInt(targetElement.find('.time-grid').last().position().left);\n            var newPosition = ui.position;\n            // On bloque le déplacement au première et dernière element\n            if (ui.position.left > firstElementPos) {\n                newPosition.left = firstElementPos;\n            }\n            else if (targetElement.width() - lastElementPos > ui.position.left) {\n                newPosition.left = targetElement.width() - lastElementPos;\n            }\n\n            return newPosition;\n        },\n        /**\n         * Fired on drag stop event\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onDragStop: function (event, ui) {\n            var targetElement = $(event.target);\n            var tcin = parseFloat(targetElement.attr('data-tcin'));\n            var tcout = parseFloat(targetElement.attr('data-tcout'));\n            var duration = tcout - tcin;\n            var movePos = Math.abs(ui.position.left);\n            var movPersentage = (movePos * 100) / targetElement.width();\n            var movDuration = Math.round((duration * movPersentage) / 100);\n            tcin = (ui.position.left > 0) ? tcin - movDuration : tcin + movDuration;\n            tcout = tcin + duration;\n\n            if (typeof tcin === \"number\" && typeof tcout === \"number\") {\n                // reinisialise la position\n                targetElement.css('left', \"0px\");\n                event.data.self.container.trigger(event.data.self.Class.eventTypes.UPDATE_TC, {\n                    tcin: tcin,\n                    tcout: Math.min(event.data.self.mediaDuration, tcout)\n                });\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onDragStop tcin:\" + tcin + \" tcout\" + tcout);\n                }\n            }\n        },\n        /**\n         * Fired on widows resize event\n         * @param {Object} event\n         */\n        onWindowResize: function (event) {\n            var target = $(event.target).first();\n            if (target.is(\"div\") === false) {\n                event.data.self.updateSegments(event.data.self.currentTcin, event.data.self.currentTcout);\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onWindowResize \");\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/time-axis-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle the time axe component\n * @class TimeAxisComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.timeline.BaseComponent.extend(\"fr.ina.amalia.player.plugins.timeline.TimeAxisComponent\", {\n        STYLE_CLASSNAME_EXPAND_ON: 'ajs-icon-chevron-down',\n        STYLE_CLASSNAME_EXPAND_OFF: 'ajs-icon-chevron-right',\n        eventTypes: {\n            CLICK_AT_TIC: \"fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.event.clickattic\",\n            RANGE_CHANGE: \"fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.event.rangechange\",\n            CHANGE_DISPLAY: \"fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.event.changedisplay\",\n            CHANGE_TIMEAXIS_DISPLAY_STATE: \"fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.event.changetimeaxisdisplaystate\"\n        }\n    },\n    {\n        /**\n         * Start time code\n         * @property tcin\n         * @type {Number}\n         * @default 0\n         */\n        tcin: 0,\n        /**\n         * End time code\n         * @property tcout\n         * @type {Number}\n         * @default 0\n         */\n        tcout: 0,\n        /**\n         * Media duration\n         * @property duration\n         * @type {Number}\n         * @default 0\n         */\n        duration: 0,\n        /**\n         * Instance of tic component\n         * @property segmentsGenerator\n         * @type {Object}\n         * @default null\n         */\n        segmentsGenerator: null,\n        /**\n         * Instance of zoom component\n         * @property timeZoomComponent\n         * @type {Object}\n         * @default null\n         */\n        timeZoomComponent: null,\n        /**\n         * Start time code\n         * @property currentTcin\n         * @type {Number}\n         * @default 0\n         */\n        currentTcin: 0,\n        /**\n         * End time code\n         * @property currentTcout\n         * @type {Number}\n         * @default 0\n         */\n        currentTcout: 0,\n        /**\n         * Init this class\n         * @method init\n         * @param {Object} settings\n         */\n        init: function (settings) {\n\n            this.settings = $.extend({\n                    debug: false,\n                    container: null,\n                    duration: 0,\n                    expand: false,\n                    tcOffset: 0\n                },\n                settings || {});\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.mainContainer = this.settings.container;\n            this.tcin = parseFloat(this.settings.tcOffset);\n            this.currentTcin = parseFloat(this.settings.tcOffset);\n            this.tcout = parseFloat(this.settings.duration) + parseFloat(this.settings.tcOffset);\n            this.currentTcout = parseFloat(this.settings.duration) + parseFloat(this.settings.tcOffset);\n            this.duration = parseFloat(this.settings.duration);\n            this.segmentsGenerator = null;\n            this.timeZoomComponent = null;\n\n            if (typeof this.mainContainer !== \"object\") {\n                throw new Error(\"Your container is empty.\");\n            }\n            this.initialize();\n\n        },\n        /**\n         * Initialize this class and create container\n         * @method initialize\n         */\n        initialize: function () {\n            this.createTimeAxis();\n            if (this.settings.expand === true) {\n                this.createControleBar();\n                this.createLabelContainer();\n            }\n        },\n        /**\n         * Return start time code\n         * @returns {Number}\n         */\n        getTcin: function () {\n            return this.tcin;\n        },\n        /**\n         * Return end time code\n         * @returns {Number}\n         */\n        getTcout: function () {\n            return this.tcout;\n        },\n        /**\n         * In charge to create control bar container\n         * @method createControleBar\n         */\n        createControleBar: function () {\n            var toolbarContainer = $('<div>', {\n                'class': 'toolbar-container'\n            });\n            // expend button\n            var expandBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_EXPAND, \"expand-btn\");\n            expandBtn.on('click', {\n                    self: this\n                },\n                this.onClickExpandBtn);\n            expandBtn.addClass(this.Class.STYLE_CLASSNAME_EXPAND_ON);\n            toolbarContainer.append(expandBtn);\n\n            this.mainContainer.append(toolbarContainer);\n        },\n        /**\n         * In charge to create label container\n         * @method createLabelContainer\n         */\n        createLabelContainer: function () {\n            var timeAxisLabel = $('<div>', {\n                'class': 'timeaxis-label'\n            });\n            timeAxisLabel.append($('<p>', {\n                'class': 'label',\n                'text': fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_TIMEAXIS\n            }));\n            this.mainContainer.append(timeAxisLabel);\n        },\n        /**\n         * In charge to create time axe component\n         * @method createTimeAxis\n         */\n        createTimeAxis: function () {\n            var lineContent = $('<div>', {\n                'class': 'line-content'\n            });\n            var line = $('<div>', {\n                'class': 'line'\n            });\n\n            // /tools bar\n            var toolsbarContainer = $('<div>', {\n                'class': 'toolsbar'\n            });\n            this.createToolsBar(toolsbarContainer);\n\n            var timeAxis = $('<div>', {\n                'class': 'module-timeaxis'\n            });\n\n            timeAxis.append(line);\n            timeAxis.append(lineContent);\n            // add to main container\n            this.mainContainer.append(timeAxis);\n            this.mainContainer.append(toolsbarContainer);\n\n            this.timeZoomComponent = new fr.ina.amalia.player.plugins.timeline.ZoomComponent(lineContent, this.duration, this.settings);\n            // init tic component\n            this.segmentsGenerator = new fr.ina.amalia.player.plugins.timeline.TicComponent(lineContent, this.settings);\n\n            timeAxis.on(fr.ina.amalia.player.plugins.timeline.ZoomComponent.eventTypes.CHANGE, {\n                self: this\n            }, this.onTimeRangeChange);\n\n            this.mainContainer.on('mousewheel', {\n                self: this\n            }, this.onMousewheel);\n            this.mainContainer.on('DOMMouseScroll', {\n                self: this\n            }, this.onDOMMouseScroll);\n\n            // add event listener on tic click event\n            lineContent.on(fr.ina.amalia.player.plugins.timeline.TicComponent.eventTypes.CLICK, {\n                self: this\n            }, this.onClickAtTic);\n            // add event listener on update time code\n            lineContent.on(fr.ina.amalia.player.plugins.timeline.TicComponent.eventTypes.UPDATE_TC, {\n                self: this\n            }, this.onUpdateTicRange);\n\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"createTimeAxis\");\n            }\n        },\n        /**\n         * In charge to create tools bar container\n         * @method createToolsBar\n         * @param {Object} container\n         */\n        createToolsBar: function (container) {\n            var self = this;\n            var rowContainer = $('<div>', {\n                class: 'ajs-row'\n            });\n            var leftContainer = $('<div>', {\n                class: 'ajs-col leftContainer'\n            });\n            var middleContainer = $('<div>', {\n                class: 'ajs-col middleContainer'\n            });\n            var rightContainer = $('<div>', {\n                class: 'ajs-col rightContainer'\n            });\n\n            var zoomInBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_ZOOM_IN, 'plus');\n            zoomInBtn.on('click', {\n                self: this\n            }, this.onZoomIn);\n            middleContainer.append(zoomInBtn);\n            var zoomOutBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_ZOOM_OUT, 'minus');\n            zoomOutBtn.on('click', {\n                self: this\n            }, this.onZoomOut);\n            middleContainer.append(zoomOutBtn);\n            var changeDisplayBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_CHANGE_DISPLAY, 'arrows-v');\n            changeDisplayBtn.on('click', {\n                self: this\n            }, this.onChangeDisplay);\n            middleContainer.append(changeDisplayBtn);\n            ///Slider Left and Right\n            var slideLeftInterval = null;\n            var slideLeftBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SLIDE_LEFT, 'chevron-left');\n            slideLeftBtn.on('click', $.proxy(this.onSlideLeft, this));\n            slideLeftBtn.on('mousedown', function () {\n                slideLeftInterval = setInterval(function () {\n                    self.onSlideLeft();\n                }, 75);\n            });\n            slideLeftBtn.on('mouseup', function () {\n                clearTimeout(slideLeftInterval);\n            });\n            slideLeftBtn.addClass('pull-left');\n            leftContainer.append(slideLeftBtn);\n\n            var slideRightInterval = null;\n            var slideRightBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SLIDE_RIGHT, 'chevron-right');\n            slideRightBtn.on('click', $.proxy(this.onSlideRight, this));\n            slideRightBtn.on('mousedown', function () {\n                slideRightInterval = setInterval(function () {\n                    self.onSlideRight();\n                }, 75);\n            });\n            slideRightBtn.on('mouseup', function () {\n                clearTimeout(slideRightInterval);\n            });\n\n            slideRightBtn.addClass('pull-right');\n            rightContainer.append(slideRightBtn);\n            rowContainer.append(leftContainer);\n            rowContainer.append(middleContainer);\n            rowContainer.append(rightContainer);\n            container.append(rowContainer);\n\n        },\n        /**\n         * In charge to set time code\n         * @method\n         * @param {Number} tcin\n         * @param {Number} tcout\n         */\n        setZoomTc: function (tcin, tcout) {\n            this.timeZoomComponent.setTc(parseFloat(tcin * 1000), parseFloat(tcout * 1000));\n        },\n        /**\n         * Handle a zoom event\n         * @param {String} _state up/down\n         */\n        zoom: function (_state, tc) {\n            var state = (typeof _state === \"undefined\") ? 'in' : _state;\n            // Default zoom out\n            if (state === \"in\") {\n                this.timeZoomComponent.zoomIn(tc);\n            }\n            else {\n                this.timeZoomComponent.zoomOut(tc);\n            }\n        },\n        /**\n         * Handle a slide event\n         * @param {String} _state left/right\n         */\n        slide: function (_state) {\n            var state = (typeof _state === \"undefined\") ? 'left' : _state;\n            // Default slide right\n            if (state === \"left\") {\n                this.timeZoomComponent.slideLeft();\n            }\n            else {\n                this.timeZoomComponent.slideRight();\n            }\n        },\n        // set events\n        /**\n         * Fired on zoom event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onTimeRangeChange: function (event, data) {\n            event.data.self.currentTcin = parseFloat(data.tcin);\n            event.data.self.currentTcout = parseFloat(data.tcout);\n            event.data.self.segmentsGenerator.updateSegments(data.tcin, data.tcout);\n            // Trigger\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.RANGE_CHANGE, {\n                tcin: data.tcin / 1000,\n                tcout: data.tcout / 1000\n            });\n        },\n        /**\n         * Fired on zoom in event\n         * @param {Object} event\n         */\n        onZoomIn: function (event) {\n            var tc = (event.data.self.currentTcout - event.data.self.currentTcin) / 2 + event.data.self.currentTcin;\n            event.data.self.zoom(\"in\", tc);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onZoomIn\");\n            }\n        },\n        /**\n         * Fired on zoom out event\n         * @param {Object} event\n         */\n        onZoomOut: function (event) {\n            var tc = (event.data.self.currentTcout - event.data.self.currentTcin) / 2 + event.data.self.currentTcin;\n            event.data.self.zoom(\"out\", tc);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onZoomOut\");\n            }\n        },\n        /**\n         * Fired slide left event\n         * @param {Object} event\n         */\n        onSlideLeft: function () {\n            this.slide(\"left\");\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"slideLeft\");\n            }\n        },\n        /**\n         * Fired slide right event\n         * @param {Object} event\n         */\n        onSlideRight: function () {\n            this.slide(\"right\");\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"slideRight\");\n            }\n        },\n        /**\n         * Fired on display change event\n         * @param {Object} event\n         */\n        onChangeDisplay: function (event) {\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.CHANGE_DISPLAY);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onChangeDisplay\");\n            }\n        },\n        /**\n         * return tc of event\n         * @method getTcOfEvent\n         * @param {Object} event\n         */\n        getTcOfEvent: function (event) {\n            var currentTarget = $(event.currentTarget);\n            return ((event.data.self.currentTcout - event.data.self.currentTcin) * (event.clientX - currentTarget.offset().left) / currentTarget.width()) + event.data.self.currentTcin;\n        },\n        /**\n         * Fired on mouse scroll\n         * @method onDOMMouseScroll\n         * @param {Object} event\n         */\n        onDOMMouseScroll: function (event) {\n            // prevent default process\n            event.preventDefault();\n            var rot = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta : event.originalEvent.detail;\n            var tc = event.data.self.getTcOfEvent(event);\n            if (typeof rot === \"number\") {\n                if (rot < 0) {\n                    event.data.self.zoom(\"in\", tc);\n                }\n                else {\n                    event.data.self.zoom(\"out\", tc);\n                }\n            }\n\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onDOMMouseScroll\");\n            }\n        },\n\n        /**\n         * Fired on Mousewheel event\n         * @method onMousewheel\n         * @param {Object} event\n         */\n        onMousewheel: function (event) {\n            // prevent default process\n            event.preventDefault();\n            var rot = (event.originalEvent.wheelDelta) ? event.originalEvent.wheelDelta : event.originalEvent.detail;\n            var tc = event.data.self.getTcOfEvent(event);\n            if (typeof rot === \"number\") {\n                if (rot > 0) {\n                    event.data.self.zoom(\"in\", tc);\n                }\n                else {\n                    event.data.self.zoom(\"out\", tc);\n                }\n            }\n\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onMousewheel\");\n            }\n        },\n        /**\n         * Fired on click to tic label\n         * @see TicComponent\n         * @method onClickAtTic\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onClickAtTic: function (event, data) {\n            if (data.hasOwnProperty('tc') === true && typeof data.tc === \"number\") {\n                event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.CLICK_AT_TIC, {\n                    tc: parseFloat(data.tc)\n                });\n            }\n        },\n        /**\n         * Fired on tic range change\n         * @see TicComponent\n         * @method onUpdateTicRange\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onUpdateTicRange: function (event, data) {\n            if (data.hasOwnProperty('tcin') === true && data.hasOwnProperty('tcout') === true) {\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onUpdateTicRange\");\n                }\n                event.data.self.timeZoomComponent.setTc(data.tcin, data.tcout);\n            }\n        },\n        /**\n         * Fired on click to expand button\n         * @method onClickExpandBtn\n         * @param {Object} event\n         */\n        onClickExpandBtn: function (event) {\n            try {\n                if (event.data.self.mainContainer.hasClass('off')) {\n                    event.data.self.mainContainer.removeClass(\"off\").addClass('on');\n                    event.data.self.mainContainer.find('.expand-btn.plugin-btn').removeClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_OFF).addClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_ON);\n                    event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.CHANGE_TIMEAXIS_DISPLAY_STATE, {\n                        state: true\n                    });\n                }\n                else {\n                    event.data.self.mainContainer.removeClass(\"on\").addClass('off');\n                    event.data.self.mainContainer.find('.expand-btn.plugin-btn').removeClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_ON).addClass(event.data.self.Class.STYLE_CLASSNAME_EXPAND_OFF);\n                    event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.CHANGE_TIMEAXIS_DISPLAY_STATE, {\n                        state: false\n                    });\n                }\n\n            }\n            catch (e) {\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.error(event.data.self.Class.fullName + \"onClickExpandBtn : \" + e.toString());\n                }\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/timeline.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to timeline plugin for visualize the keyframes with cue point,\n * segment,image ,histogram  and visual components.\n * @class TimelinePlugin\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBaseMultiBlocks.extend(\"fr.ina.amalia.player.plugins.TimelinePlugin\", {\n        eventTypes: {\n            TIME_CHANGE: \"fr.ina.amalia.player.plugins.timeline.TIME_CHANGE\"\n        },\n        classCss: \"ajs-plugin plugin-timeline\",\n        style: \"\",\n        componentDefaultHeight: 110\n    },\n    {\n        /**\n         * Dom element for time cursor\n         * @property timeCursor\n         * @type {Object}\n         */\n        timeCursor: null,\n        /**\n         * Main time cursor\n         * @property timeProgressContainer\n         * @type {Object}\n         */\n        timeProgressContainer: null,\n        /**\n         * time axe cursor component container\n         * @property timeAxisComponentContainer\n         * @type {Object}\n         */\n        timeAxisComponentContainer: null,\n        /**\n         * Instance de la classe TimeAxisComponent.\n         * @property timeAxisComponent\n         * @type {Object} fr.ina.amalia.player.plugins.timeline.TimeAxisComponent\n         * @default null\n         */\n        timeAxisComponent: null,\n        /**\n         * Contains all the components\n         * @property componentsContainer\n         * @type {Object}\n         */\n        componentsContainer: null,\n        /**\n         * Nav bar container\n         * @property navBarContainer\n         * @type {Object}\n         */\n        navBarContainer: null,\n        /**\n         * list of zoom range\n         * @property navBarContainer\n         * @type {Object}\n         */\n        zoomRangeLevels: null,\n        /**\n         * zoom level\n         * @property navBarContainer\n         * @type {Object}\n         */\n        zoomLevel: 0,\n        /**\n         * Number of line to be displayed\n         * @property displayLinesNb\n         * @type {Object}\n         */\n        displayLinesNb: 0,\n        /**\n         * list of components\n         * @property listOfComponents\n         * @default []\n         */\n        listOfComponents: [],\n        /**\n         * Start time code\n         * @property tcin\n         * @default 0\n         */\n        tcin: 0,\n        /**\n         * End time code\n         * @property tcout\n         * @default 0\n         */\n        tcout: 0,\n        /**\n         * Start zoom tim ecode\n         * @property zTcin\n         * @default 0\n         */\n        zTcin: 0,\n        /**\n         * End zoom time code\n         * @property zTcout\n         * @default 0\n         */\n        zTcout: 0,\n        /**\n         * Default height of time axe\n         * @property timeAxisHeight\n         * @default 150\n         */\n        timeAxisHeight: 150,\n        /**\n         * Tool tip configuration\n         * @property tooltipConfiguration\n         * @default 150\n         */\n        tooltipConfiguration: {},\n        /**\n         * Instance of local storage\n         * @property localStorageManager\n         * @default null\n         */\n        localStorageManager: null,\n        /**\n         * true if load data started anw\n         * @property loadDataStarted\n         * @default null\n         */\n        loadDataStarted: false,\n        /**\n         * List of id to deal\n         * @property loadDataStarted\n         * @default null\n         */\n        dataToDeal: null,\n        /**\n         * Line configuration\n         * @property loadDataStarted\n         * @default null\n         */\n        settingsListOfComponents: [],\n        /**\n         * True if editing mode is enabled\n         * @property editingMode\n         * @default null\n         */\n        editingMode: false,\n        /**\n         * Instance of metadata manager\n         * @property metadataManager\n         * @default null\n         */\n        metadataManager: null,\n        /**\n         * Initialize this class with default parameters\n         * @method initialize\n         */\n        initialize: function () {\n            this.listOfMetadataTypes = [];\n            this.notManagedMetadataIds = [];\n            this.managedMetadataIds = [];\n            this.listOfComponents = [];\n            this.tooltipConfiguration = $.extend(true, fr.ina.amalia.player.plugins.timeline.DefaultConfiguration.tooltipConfiguration, {});\n            // Default configuration\n            this.settings = $.extend({\n                    debug: this.settings.debug,\n                    timeFormat: 'mms',\n                    internalPlugin: false,\n                    // Line configuration\n                    listOfLines: {},\n                    // Number of line to be displayed\n                    displayLines: 3,\n                    // Default zoom level\n                    nbZoomLevel: 3,\n                    // Default zoom configuration\n                    zoomCoef: [1 / 4, 1 / 16, 1 / 64, 1 / 256],\n                    // Attribute name use for zoom\n                    zoomProperty: 'tclevel',\n                    // To enable the time axis\n                    timeaxis: true,\n                    displayState: '',\n                    // To enable resizable mode\n                    resizable: false,\n                    viewZoomSync: false,\n                    viewZoomSyncOffset: 1,\n                    mouseWheelCoef: 5,\n                    // To enable time cursor\n                    timecursor: true,\n                    timeaxisExpandable: false,\n                    editingMode: false,\n                    thumbRootDirectory: this.settings.thumbRootDirectory,\n                    thumbDirectory: '',\n                    callback: '',\n                    callbackStyle: '',\n                    lineDisplayMode: fr.ina.amalia.player.plugins.PluginBaseMultiBlocks.METADATA_DISPLAY_TYPE.STATIC\n                },\n                this.settings.parameters || {});\n\n            this.tcin = this.mediaPlayer.getTcOffset();\n            this.tcout = this.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset();\n            this.zTcin = this.mediaPlayer.getTcOffset();\n            this.zTcout = this.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset();\n            this.metadataManager = this.mediaPlayer.getMetadataManager();\n            this.loadDataStarted = false;\n            this.editingMode = this.settings.editingMode;\n            // Set default data type managed by this plugin.\n            this.registerMetadataTypes();\n            this.registerPluginTypes();\n\n            // Line configuration\n            this.settingsListOfComponents = $.extend(true, [], this.settings.listOfLines);\n\n            if (this.settings.timecursor === true) {\n                this.createTimeCursor();\n            }\n\n            // Enable time axe\n            if (this.settings.timeaxis === true) {\n                this.timeAxisComponentContainer = $('<div>', {\n                    class: 'timeaxis'\n                });\n                this.createTimeProgressContainer();\n            }\n            else {\n                this.timeProgressContainer = null;\n            }\n\n            this.componentsContainer = $('<div>', {\n                class: 'components',\n                style: 'height:' + (this.pluginContainer.height() - this.timeAxisHeight) + 'px;'\n            });\n            // jquery ui sortable\n            this.componentsContainer.sortable({\n                handle: '.sort-btn'\n            });\n            // zoom\n            this.zoomRangeLevels = this.getZoomRangeLevels(this.tcout, this.settings.nbZoomLevel, this.settings.zoomCoef);\n\n\n            this.pluginContainer.append(this.timeAxisComponentContainer);\n            this.pluginContainer.append(this.componentsContainer);\n            this.createTimlineNavBar(this.settings.listOfLines.length);\n            // Instance of local storage\n            this.localStorageManager = new fr.ina.amalia.player.LocalStorageManager({});\n            // update tool tips\n            this.pluginContainer.find('.plugin-btn').tooltip(\"option\", \"disabled\", (this.localStorageManager.hasItem('help-tooltip') === false) ? false : this.localStorageManager.getItem('help-tooltip'));\n            // Enabling resize mode\n            if (this.settings.resizable === true) {\n                this.pluginContainer.resizable({\n                    handles: 's',\n                    create: function (event) {\n                        $(event.target).find('div.ui-resizable-handle').addClass('ajs-icon ajs-icon-ellipsis-h');\n                    },\n                    start: this.onResizeStart,\n                    resize: this.onResize\n                });\n            }\n            this.resizeComponentsHeight();\n            if (this.timeAxisComponent !== null) {\n                this.timeAxisComponent.setZoomTc(this.zTcin, this.zTcout);\n            }\n            this.defineListeners();\n        },\n        /**\n         * In charge to register data type managed by this plugin.\n         * @method registerMetadataTypes\n         */\n        registerMetadataTypes: function () {\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.DETECTION);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.SEGMENTATION);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.TRANSCRIPTION);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.SYNCHRONIZED_TEXT);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.KEYFRAMES);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.HISTOGRAM);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_DETECTION);\n            this.addManagedMetadataType(fr.ina.amalia.player.PluginBindingManager.dataTypes.VISUAL_TRACKING);\n        },\n        /**\n         * In charge to register this plugin types\n         * @method registerPluginTypes\n         */\n        registerPluginTypes: function () {\n            this.registerPluginType(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_CUEPOINT);\n            this.registerPluginType(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_SEGMENT);\n            this.registerPluginType(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_IMAGE);\n            this.registerPluginType(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_HISTOGRAM);\n            this.registerPluginType(fr.ina.amalia.player.PluginBindingManager.pluginTypes.TIMELINE_VISUAL);\n        },\n        /**\n         * In charge to create time cursor container\n         * @method createTimeCursor\n         */\n        createTimeCursor: function () {\n            this.timeCursor = $('<div>', {\n                'class': 'timeline-cursor'\n            });\n            // Add to main container\n            this.pluginContainer.append(this.timeCursor);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"createTimeCursor\");\n            }\n        },\n        /**\n         * In charge to create time progress bar container\n         * @method createTimeProgressContainer\n         */\n        createTimeProgressContainer: function () {\n            this.timeProgressContainer = $('<div>', {\n                'class': 'timeline-progress-container'\n            });\n            this.initializeTimeAxisComponent();\n            // Add to main container\n            this.pluginContainer.append(this.timeProgressContainer);\n\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"createTimeProgressContainer\");\n            }\n        },\n        /**\n         * In charge to create time axe navigation bar\n         * @param {Numbre} nbItems\n         */\n        createTimlineNavBar: function (nbItems) {\n            this.navBarContainer = $('<div>', {\n                'class': 'module-nav-bar-container '\n            });\n            var rowContainer = $('<div>', {\n                class: 'ajs-row toolsbar'\n            });\n            var leftContainer = $('<div>', {\n                class: 'ajs-col-3 leftContainer'\n            });\n            var middleContainer = $('<div>', {\n                class: 'ajs-col-6 middleContainer'\n            });\n            var rightContainer = $('<div>', {\n                class: 'ajs-col-3 rightContainer'\n            });\n            // boutons\n            var scrollDownBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SCROLL_DOWN, 'chevron-down scroll-btn');\n            scrollDownBtn.on('click', {\n                    self: this\n                },\n                this.onScrollDown);\n            var scrollUpBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SCROLL_UP, 'chevron-up scroll-btn');\n            scrollUpBtn.on('click', {\n                    self: this\n                },\n                this.onScrollUp);\n            middleContainer.append(scrollUpBtn);\n            middleContainer.append(scrollDownBtn);\n            leftContainer.append($('<div>', {\n                class: 'info',\n                html: '<span class=\"count\">' + nbItems + '</span> ' + fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_ELEMENTS\n            }));\n            rightContainer.append(this.createDropdownMenu());\n            rowContainer.append(leftContainer);\n            rowContainer.append(middleContainer);\n            rowContainer.append(rightContainer);\n            this.navBarContainer.append(rowContainer);\n            // Add to main container\n            this.pluginContainer.append(this.navBarContainer);\n            // Initialize all line with configuration\n            if (this.settings.lineDisplayMode < 3) {\n                this.createComponentsWithList(this.settingsListOfComponents);\n            }\n        },\n        /**\n         * Create button with tooltip\n         * @method createButton\n         * @param {String} name\n         * @param {String} icon\n         */\n        createButton: function (name, icon) {\n            return $('<button>', {\n                title: name,\n                class: \"plugin-btn ajs-icon ajs-icon-\" + icon\n            }).tooltip(this.tooltipConfiguration);\n        },\n        /**\n         * Create drop down menu\n         * @method createDropdownMenu\n         */\n        createDropdownMenu: function () {\n            var dropup = $('<div>', {\n                class: 'config-menu btn-group'\n            });\n            var dropupCtrl = $('<span>', {\n                class: \"config-btn\",\n                'data-toggle': \"dropdown\"\n            }).on('click', function (e) {\n                $(e.currentTarget).parent().find('.config-menu-list').show();\n            });\n            dropupCtrl.append($('<span>', {\n                class: \"ajs-icon ajs-icon-cog\"\n            }));\n            dropup.append(dropupCtrl);\n            var menuItems = $(\"<ul>\", {\n                'class': 'config-menu-list',\n                \"role\": \"menu\"\n            }).hide().on('mouseleave', function (e) {\n                $(e.currentTarget).hide();\n            });\n            // Item permettant de contrôler l'affichage des infobulles\n            var itemHideTootips = $('<li>', {\n                text: fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_SHOW_HIDE_TOOTIP\n            }).on('click', {\n                    self: this\n                },\n                this.onChangeToogleStatus);\n            menuItems.append(itemHideTootips);\n            dropup.append(menuItems);\n            return dropup;\n        },\n        /**\n         * Initialize timeaxis component\n         * @method initializeTimeAxisComponent\n         */\n        initializeTimeAxisComponent: function () {\n            var componentSettings = $.extend({\n                debug: this.settings.debug,\n                container: this.timeAxisComponentContainer,\n                duration: this.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset(),\n                expand: this.settings.timeaxisExpandable,\n                tcOffset: this.mediaPlayer.getTcOffset(),\n                timeFormat: this.settings.timeFormat,\n                framerate: this.settings.framerate,\n                displayState: this.settings.displayState\n            }, this.settings.timeAxisSettings || {});\n            try {\n                this.timeAxisComponent = new fr.ina.amalia.player.plugins.timeline.TimeAxisComponent(componentSettings);\n                this.timeAxisComponent.getContainer().on(fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.eventTypes.RANGE_CHANGE, {\n                    self: this\n                }, this.onRangeChange);\n                this.timeAxisComponent.getContainer().on(fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.eventTypes.CHANGE_DISPLAY, {\n                    self: this\n                }, this.onDisplayStateChange);\n                this.timeAxisComponent.getContainer().on(fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.eventTypes.CHANGE_TIMEAXIS_DISPLAY_STATE, {\n                    self: this\n                }, this.onTimeaxisDisplayStateChange);\n                this.timeAxisComponent.getContainer().on(fr.ina.amalia.player.plugins.timeline.TimeAxisComponent.eventTypes.CLICK_AT_TIC, {\n                    self: this\n                }, this.onClickTc);\n            }\n            catch (error) {\n                if (this.logger !== null) {\n                    this.logger.warn(error.stack);\n                }\n                this.timeAxisComponent = null;\n            }\n        },\n        /**\n         * Add player events listener\n         * @method definePlayerListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n\n            // On time change\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this\n            }, this.onTimeupdate);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.BEGIN_DATA_CHANGE, {\n                self: this\n            }, this.onBeginDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.END_DATA_CHANGE, {\n                self: this\n            }, this.onEndDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                self: this\n            }, this.onDataChange);\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.ZOOM_RANGE_CHANGE, {\n                self: this\n            }, this.onZoomRangeChange);\n\n            //Selected metadata changed event\n            mainContainer.on(fr.ina.amalia.player.PlayerEventType.SELECTED_METADATA_CHANGE, {\n                self: this\n            }, this.onSelectedMetadataChange);\n\n            // Add data change events\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.CuepointsComponent.eventTypes.DATA_CHANGE, {\n                self: this\n            }, this.onCuepointDataChange);\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.SegmentsComponent.eventTypes.DATA_CHANGE, {\n                self: this\n            }, this.onSegmentDataChange);\n\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.components.FocusComponent.eventTypes.ZOOM_ZONE_CHANGE, {\n                self: this\n            }, this.onZoomZoneChangeWithFocusComponent);\n            // /onCuepointDataChange\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.BaseComponent.CLICK_SELECT, {\n                self: this\n            }, this.onSelectItem);\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.BaseComponent.CLICK_REMOVE_SELECT_ITEM, {\n                self: this\n            }, this.onRemoveSelectItem);\n\n            ///Visual component\n\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.VisualComponent.eventTypes.DATA_CHANGE, {\n                self: this\n            }, this.onVisualComponentDataChange);\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.VisualComponent.eventTypes.BIND, {\n                self: this\n            }, this.onVisualComponentBindMetadata);\n            this.pluginContainer.on(fr.ina.amalia.player.plugins.timeline.VisualComponent.eventTypes.UNBIND, {\n                self: this\n            }, this.onVisualComponentUnBindMetadata);\n\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * Update zoom tcin and tcout\n         * @method updateZoomChange\n         * @param {Number} zTcin\n         * @param {Number} zTcout\n         */\n        updateZoomChange: function (zTcin, zTcout) {\n            if (this.zTcin !== zTcin || this.zTcout !== zTcout) {\n                this.zTcin = Math.max(0, parseFloat(zTcin));\n                this.zTcout = Math.min(this.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset(), parseFloat(zTcout));\n                this.mediaPlayer.setZoomTc(this.zTcin, this.zTcout, this.eventTag);\n                if (this.timeAxisComponent !== null) {\n                    this.timeAxisComponent.setZoomTc(this.zTcin, this.zTcout);\n                }\n                this.updateRange(this.zTcin, this.zTcout);\n                if (this.settings.timecursor === true) {\n                    this.updateTimelinePos(parseFloat(this.mediaPlayer.getCurrentTime()));\n                }\n            }\n        },\n        /**\n         * Update time range\n         * @param {Number} tcin\n         * @param {Number} tcout\n         */\n        updateRange: function (tcin, tcout) {\n            this.tcin = tcin;\n            this.tcout = tcout;\n            this.zoomLevel = this.getZoomRangeLevel(this.tcin, this.tcout);\n            this.updateAllBlocks();\n        },\n        /**\n         * Update time cursor position\n         * @method updateTimelinePos\n         * @param {Object} currentTime\n         */\n        updateTimelinePos: function (currentTime) {\n            // item tc\n            var tc = parseFloat(currentTime);\n            // global tc\n            var gtc = this.tcout - this.tcin;\n            var percentWidth = ((tc - this.tcin) * 100) / gtc;\n\n            if (currentTime > this.tcin && currentTime < this.tcout) {\n                this.timeCursor.show();\n                this.timeCursor.data('tc', currentTime);\n                this.timeCursor.css('left', percentWidth + '%');\n                if (this.timeProgressContainer !== null) {\n                    this.timeProgressContainer.show();\n                    this.timeProgressContainer.data('tc', currentTime);\n                    this.timeProgressContainer.css('right', (100 - percentWidth) + '%');\n                }\n            }\n            else if (currentTime > this.tcin) {\n                this.timeCursor.hide();\n                if (this.timeProgressContainer !== null) {\n                    this.timeProgressContainer.css('right', '0%');\n                }\n            }\n            else {\n                this.timeCursor.hide();\n                if (this.timeProgressContainer !== null) {\n                    this.timeProgressContainer.hide();\n                }\n            }\n        },\n        /**\n         * Get zoom range level with timecode\n         * @param {Number} tcin\n         * @param {Number} tcout\n         */\n        getZoomRangeLevel: function (tcin, tcout) {\n            var tc = tcout - tcin;\n            for (var i = 0;\n                 i < this.zoomRangeLevels.length;\n                 i++) {\n                if (tc > this.zoomRangeLevels[i].start && tc < this.zoomRangeLevels[i].end) {\n                    return this.zoomRangeLevels[i].level;\n                }\n            }\n            return 0;\n        },\n        /**\n         * Get all zoom range level with timecode and zoom coefficient\n         * @param {Number} tc time code\n         * @param {Number} nbLevel level numbre\n         * @param {Object} zoomCoef\n         */\n        getZoomRangeLevels: function (tc, nbLevel, zoomCoef) {\n            var zoomTcRange = [];\n            var zoomRange = null;\n            for (var i = 0;\n                 i < nbLevel;\n                 i++) {\n                zoomRange = {\n                    level: i,\n                    start: (i < nbLevel - 1) ? zoomCoef[i] * tc : 0,\n                    end: (i === 0) ? tc : zoomCoef[i - 1] * tc\n                };\n                zoomRange.tc = zoomRange.end - zoomRange.start;\n                zoomTcRange.push(zoomRange);\n            }\n            return zoomTcRange;\n        },\n        /**\n         * In charge to create cue point component\n         * @method createCuepointsComponent\n         * @param {String} container\n         * @param {Object} settings\n         */\n        createComponent: function (container, settings) {\n            var componentSettings = $.extend({\n                    debug: this.settings.debug,\n                    container: container,\n                    duration: this.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset(),\n                    tcOffset: this.mediaPlayer.getTcOffset(),\n                    timeFormat: this.settings.timeFormat,\n                    framerate: this.settings.framerate,\n                    displayState: this.settings.displayState,\n                    callback: this.settings.callback,\n                    callbackStyle: this.settings.callbackStyle,\n                    thumbDirectory: this.settings.thumbRootDirectory + this.settings.thumbDirectory\n                },\n                settings || {});\n            var component = null;\n            try {\n                if (componentSettings.hasOwnProperty('className') === true) {\n                    /* jslint evil: true */\n                    component = eval(\"new \" + componentSettings.className + '(componentSettings,this.mediaPlayer)');\n                }\n                else {\n                    /* jslint evil: true */\n                    component = eval(\"new \" + this.getComponentType(settings.type) + '(componentSettings,this.mediaPlayer)');\n                }\n            }\n            catch (error) {\n                if (this.logger !== null) {\n                    this.logger.warn(error.stack);\n                }\n            }\n            return component;\n        },\n        /**\n         * Return the component default class name of the component by type\n         * @param {String} type line type\n         */\n        getComponentType: function (type) {\n            var className = '';\n            switch (type) {\n                // create image component\n                case \"image\" :\n                    className = 'fr.ina.amalia.player.plugins.timeline.ImagesComponent';\n                    break;\n                // create segment component\n                case \"segment\" :\n                    className = 'fr.ina.amalia.player.plugins.timeline.SegmentsComponent';\n                    break;\n                // create histogram component\n                case \"histogram\" :\n                    className = 'fr.ina.amalia.player.plugins.timeline.HistogramComponent';\n                    break;\n                case \"visual\" :\n                    className = 'fr.ina.amalia.player.plugins.timeline.VisualComponent';\n                    break;\n                default :\n                    className = 'fr.ina.amalia.player.plugins.timeline.CuepointsComponent';\n                    break;\n            }\n            return className;\n        },\n        /**\n         * In cherge to configure dynaminc line\n         * @param metadataId\n         * @param type\n         * @param shape\n         */\n        getNewLineConf: function (metadataId, type, shape) {\n            type = type.split('_')[1];\n            return {\n                title: metadataId,\n                metadataId: metadataId,\n                type: type,\n                pointNav: true,\n                icon: shape,\n                editable: this.editingMode,\n                timeFormat: this.settings.timeFormat,\n                framerate: this.settings.framerate\n            };\n        },\n        /**\n         * In charge to create components with list\n         * @method createComponentsWithList\n         * @param listOfComponents\n         */\n        createComponentsWithList: function (listOfComponents) {\n            var container = null;\n            var component = null;\n            for (var i = 0;\n                 i < listOfComponents.length;\n                 i++) {\n                var lineSettings = listOfComponents[i];\n                if (lineSettings.hasOwnProperty('title') === true && lineSettings.hasOwnProperty('type') === true && lineSettings.hasOwnProperty('metadataId') === true) {\n                    container = $('<div>', {\n                        class: \"component\"\n                    });\n                    this.componentsContainer.append(container);\n                    component = this.createComponent(container, lineSettings);\n                    if (component !== null) {\n                        component.setZoomProperty(this.settings.zoomProperty);\n                        component.setMetadataId(lineSettings.metadataId);\n                        component.setZoomLevel(this.zoomLevel);\n                        component.setTcin(this.tcin);\n                        component.setTcout(this.tcout);\n                        component.setZoomTc(this.zTcin, this.zTcout);\n                        // Set metadata id\n                        container.attr('data-metadata-id', lineSettings.metadataId);\n                        this.bindMetadataId(lineSettings.metadataId);\n                        this.listOfComponents.push(component);\n\n                        component.getContainer().on(fr.ina.amalia.player.plugins.timeline.BaseComponent.CLICK_TC, {\n                            self: this\n                        }, this.onClickTc);\n                        component.getContainer().on(fr.ina.amalia.player.plugins.timeline.BaseComponent.NAV_CLICK, {\n                            self: this,\n                            component: component\n                        }, this.onClickNavControls);\n                    }\n                }\n                else {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Error initializing the component.\");\n                    }\n                }\n            }\n            this.resizeComponentsHeight();\n            this.updateComponentsLineHeight();\n\n            this.componentsContainer.find('.component').on('click', {\n                    self: this\n                },\n                this.onSelectComponents);\n\n        },\n        /**\n         * In charge to delete component\n         * @param {String} metadataId\n         */\n        deleteComponentsWithMetadataId: function (metadataId) {\n            this.componentsContainer.find('.component[data-metadata-id=\"' + metadataId + '\"]').remove();\n            this.unbindMetadataId(metadataId);\n        },\n        /**\n         * Update all block\n         */\n        updateAllBlocks: function () {\n            var listOfObject = null;\n            for (var i = 0;\n                 i < this.listOfComponents.length;\n                 i++) {\n                var component = this.listOfComponents[i];\n                component.setZoomLevel(this.zoomLevel);\n                component.setTcin(this.tcin);\n                component.setTcout(this.tcout);\n                component.setZoomTc(this.zTcin, this.zTcout);\n                if (typeof component.removeItems === \"function\" && typeof component.addItems === \"function\" && typeof component.getMetadataId === \"function\") {\n                    component.removeItems();\n                    listOfObject = this.mediaPlayer.getMetadataById(component.getMetadataId());\n                    if (typeof listOfObject !== \"undefined\" && listOfObject !== null && listOfObject.hasOwnProperty('length') && listOfObject.length > 0) {\n                        component.addItems(listOfObject);\n                    }\n                }\n            }\n\n        },\n        /**\n         * In charge to update block by id metadata\n         * @param {String} id\n         * @param {String} action\n         */\n        updateBlock: function (id, action) {\n            for (var i = 0;\n                 i < this.listOfComponents.length;\n                 i++) {\n                var component = this.listOfComponents[i];\n\n                if (component.getMetadataId() === id) {\n                    if (typeof component.removeItems === \"function\" && typeof component.addItems === \"function\" && typeof component.getMetadataId === \"function\") {\n                        component.removeItems();\n                        var listOfObject = this.mediaPlayer.getMetadataById(component.getMetadataId());\n                        if (this.settings.lineDisplayMode > 1) {\n                            var metadataObject = this.mediaPlayer.getBlockMetadata(id.toString());\n                            component.setTitle(metadataObject.label);\n                            if (metadataObject.hasOwnProperty('viewControl') && metadataObject.viewControl !== null) {\n                                component.setShape(metadataObject.viewControl.hasOwnProperty('shape') ? metadataObject.viewControl.shape : '');\n                                component.setColor(metadataObject.viewControl.hasOwnProperty('color') ? metadataObject.viewControl.color : '');\n                            }\n                            if (action === \"replace-all\" && typeof component.setHistogramIsCreated === \"function\") {\n                                component.setHistogramIsCreated(false);\n                            }\n                        }\n                        if (typeof listOfObject !== \"undefined\" && listOfObject !== null && listOfObject.hasOwnProperty('length') && listOfObject.length > 0) {\n                            component.addItems(listOfObject);\n                        }\n                    }\n                }\n            }\n\n        },\n        /**\n         * In charge to update list of ids\n         * @param listOfIds list of ids\n         */\n        updateBlocks: function (listOfIds) {\n            for (var i = 0;\n                 i < listOfIds.length;\n                 i++) {\n                if (this.settings.lineDisplayMode > 1 && this.isManagedMetadataId(listOfIds[i]) === false) {\n                    var metadataObject = this.mediaPlayer.getBlockMetadata(listOfIds[i]);\n                    var metadataDataType = (metadataObject !== null && metadataObject.hasOwnProperty('type')) ? metadataObject.type : '';\n                    var lineType = this.bindingManager.getLinetypeWithDataType(metadataDataType);\n\n                    if (this.bindingManager.isManagedDataType(this.uuid, metadataDataType) === true && lineType !== null) {\n                        var shape = (metadataObject !== null && metadataObject.hasOwnProperty('shape') && metadataObject.shape !== \"\") ? metadataObject.shape : 'circle';\n                        this.createComponentsWithList([\n                            this.getNewLineConf(listOfIds[i], lineType, shape)\n                        ]);\n                    }\n                    this.updateBlock(listOfIds[i]);\n                }\n                else {\n                    this.updateBlock(listOfIds[i]);\n                }\n            }\n            this.updateRange(this.tcin, this.tcout);\n        },\n        /**\n         * Update line height\n         * @method updateComponentsLineHeight\n         */\n        updateComponentsLineHeight: function () {\n            var element = this.pluginContainer;\n            var headerAndFooterHeight = parseFloat(element.find('.timeaxis').height() + element.find('.module-nav-bar-container').height());\n            var linesOffset = 2 * element.find('.component').length;\n            var lineHeight = $(element).find('.component').first().height();\n            var displayLinesNb = this.settings.displayLines;\n            element.find('.components').first().css('height', lineHeight * displayLinesNb + linesOffset); // 5\n            // offset\n            element.css(\"height\", lineHeight * displayLinesNb + linesOffset + headerAndFooterHeight);\n            element.find('.module-nav-bar-container .info span.count').text(element.find('.component').length);\n        },\n        /**\n         * In charge to update lines components\n         * @method resizeComponentsHeight\n         */\n        resizeComponentsHeight: function () {\n            var headerAndFooterHeight = parseFloat(this.pluginContainer.find('.timeaxis').height() + this.pluginContainer.find('.module-nav-bar-container').height());\n            var lineHeight = parseFloat(fr.ina.amalia.player.plugins.TimelinePlugin.componentDefaultHeight);\n            var offset = 2;\n            this.displayLinesNb = (typeof this.settings.displayLines === \"number\") ? this.settings.displayLines : this.settings.listOfLines.length;\n            if (this.displayLinesNb > this.settings.listOfLines.length) {\n                this.displayLinesNb = this.settings.listOfLines.length;\n            }\n            offset = this.displayLinesNb * offset;\n            var lh = (this.displayLinesNb * lineHeight) + this.navBarContainer.height();\n            if (this.settings.timeaxis) {\n                this.pluginContainer.css('height', lh + this.timeAxisHeight + 'px');\n            }\n            else {\n                this.pluginContainer.css('height', lh + 'px');\n            }\n            this.componentsContainer.css('height', (this.pluginContainer.height() - headerAndFooterHeight) + 'px');\n\n            var scrollBtnState = (this.componentsContainer.height() + offset >= this.componentsContainer.get(0).scrollHeight);\n            this.navBarContainer.find('.scroll-btn').toggle(!scrollBtnState);\n            if (!scrollBtnState) {\n                this.componentsContainer.on('mousewheel DOMMouseScroll', {\n                        self: this\n                    },\n                    this.onComponentsMousewheel);\n            }\n        },\n        /**\n         * In charge to move next item\n         */\n        nextItem: function () {\n            var component = null;\n            for (var i = 0; i < this.listOfComponents.length; i++) {\n                var componentContainer = this.listOfComponents[i].mainContainer;\n                if (componentContainer.hasClass('activated')) {\n                    component = this.listOfComponents[i];\n                }\n            }\n            if (component !== null) {\n                var tc = component.getNextItem(this.mediaPlayer.getCurrentTime());\n                this.mediaPlayer.pause();\n                if (tc !== null) {\n                    this.mediaPlayer.setCurrentTime(tc);\n                }\n            }\n        },\n        /**\n         * In charge to move prev item\n         * @param {Object} event\n         * @param {Object} data\n         */\n        prevItem: function () {\n            var component = null;\n            for (var i = 0; i < this.listOfComponents.length; i++) {\n                var componentContainer = this.listOfComponents[i].mainContainer;\n                if (componentContainer.hasClass('activated')) {\n                    component = this.listOfComponents[i];\n                }\n            }\n            if (component !== null) {\n                var tc = component.getPrevItem(this.mediaPlayer.getCurrentTime());\n                this.mediaPlayer.pause();\n                if (tc !== null) {\n                    this.mediaPlayer.setCurrentTime(tc);\n                }\n            }\n        },\n        // Events\n\n        /**\n         * Trigger on zoom range change\n         * @method onRangeChange\n         * @param {Object} event\n         * @param {Object} data tcin/tcout\n         */\n        onRangeChange: function (event, data) {\n            event.data.self.updateZoomChange(parseFloat(data.tcin), parseFloat(data.tcout));\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onRangeChange Tcin:\" + data.tcin + \" Tcout\" + data.tcout);\n            }\n        },\n        /**\n         * Fired on display state change event\n         * @param {Object} event\n         */\n        onDisplayStateChange: function (event) {\n            for (var i = 0;\n                 i < event.data.self.listOfComponents.length;\n                 i++) {\n                var component = event.data.self.listOfComponents[i];\n                if (typeof component.changeDisplayState === \"function\") {\n                    component.changeDisplayState();\n                }\n            }\n            // Permet de mettre à jour la hauteur du plugin\n            if (event.data.self.settings.displayLines === false) {\n                event.data.self.updateComponentsLineHeight();\n            }\n        },\n        /**\n         * Fired on time axe display change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onTimeaxisDisplayStateChange: function (event, data) {\n            if (event.data.self.timeProgressContainer !== null) {\n                if (data.state === true) {\n                    event.data.self.timeProgressContainer.css('height', '102px');\n                    event.data.self.timeProgressContainer.addClass('max').removeClass('min');\n                }\n                else {\n                    event.data.self.timeProgressContainer.css('height', '2px');\n                    event.data.self.timeProgressContainer.addClass('min').removeClass('max');\n                }\n            }\n            if (event.data.self.settings.displayLines === false) {\n                event.data.self.updateComponentsLineHeight();\n            }\n            else {\n                var element = event.data.self.pluginContainer;\n                var displayLinesNb = event.data.self.settings.displayLines;\n                var headerAndFooterHeight = parseFloat(element.find('.timeaxis').height() + element.find('.module-nav-bar-container').height());\n                var linesOffset = 2 * displayLinesNb;\n                var lineHeight = $(element).find('.component').first().height();\n                var componentsHeight = lineHeight * displayLinesNb + linesOffset;\n                element.find('.components').first().css('height', componentsHeight);\n                element.css(\"height\", componentsHeight + headerAndFooterHeight);\n            }\n        },\n        /**\n         * Fired on click at time code\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onClickTc: function (event, data) {\n            if (data.hasOwnProperty('tc') === true) {\n                event.data.self.mediaPlayer.setCurrentTime(parseFloat(data.tc));\n            }\n        },\n        /**\n         * Fired on time change event for update time cursor\n         * @method onFirstTimechange\n         * @param {Object} event\n         * @param {Object} data Données renvoyer par l'événement timeupdate.\n         */\n        onTimeupdate: function (event, data) {\n            if (event.data.self.settings.timecursor === true) {\n                event.data.self.updateTimelinePos(parseFloat(data.currentTime));\n            }\n            event.data.self.pluginContainer.trigger(fr.ina.amalia.player.plugins.TimelinePlugin.eventTypes.TIME_CHANGE, {\n                'currentTime': parseFloat(data.currentTime)\n            });\n\n            if (event.data.self.settings.viewZoomSync === true) {\n                if (parseFloat(data.currentTime) > (event.data.self.zTcout - event.data.self.settings.viewZoomSyncOffset)) {\n                    var range = (event.data.self.zTcout - event.data.self.zTcin);\n                    var zTcout = Math.min(event.data.self.mediaPlayer.getDuration() + this.mediaPlayer.getTcOffset(), event.data.self.zTcout + range);\n                    var zTcin = Math.max(0, zTcout - range);\n                    event.data.self.updateZoomChange(zTcin, zTcout);\n                }\n            }\n        },\n        /**\n         * Fired on begin data change event\n         * @method onBeginDataChange\n         * @param {Object} event\n         */\n        onBeginDataChange: function (event) {\n            event.data.self.loadDataStarted = true;\n            event.data.self.dataToDeal = [];\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onBeginDataChange\");\n            }\n        },\n        /**\n         * Fired on data change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onDataChange: function (event, data) {\n            if (event.data.self.loadDataStarted === false) {\n                if (event.data.self.editingMode) {\n                    if (data.hasOwnProperty('action') && data.action === 'deleteBlock') {\n                        event.data.self.deleteComponentsWithMetadataId(data.id);\n                    }\n                    else {\n                        if (event.data.self.isManagedMetadataId(data.id)) {\n                            event.data.self.updateBlock(data.id, data.action);\n                        }\n                        else if (event.data.self.settings.lineDisplayMode > 1) {\n                            var metadataObject = event.data.self.mediaPlayer.getBlockMetadata(data.id);\n                            var metadataDataType = (metadataObject !== null && metadataObject.hasOwnProperty('type')) ? metadataObject.type : '';\n                            var lineType = event.data.self.bindingManager.getLinetypeWithDataType(metadataDataType);\n                            var shape = (metadataObject !== null && metadataObject.hasOwnProperty('shape') && metadataObject.shape !== \"\") ? metadataObject.shape : 'circle';\n                            if (event.data.self.bindingManager.isManagedDataType(event.data.self.uuid, metadataDataType) === true && lineType !== null) {\n                                event.data.self.createComponentsWithList([\n                                    event.data.self.getNewLineConf(data.id, lineType, shape)\n                                ]);\n                                //event.data.self.bindMetadataId( data.id );\n                                event.data.self.updateBlock(data.id);\n                            }\n                        }\n                    }\n                }\n                else if (event.data.self.isManagedMetadataId(data.id)) {\n                    // Mode static\n                    event.data.self.updateBlock(data.id, data.action);\n                }\n            }\n            else {\n                // begin data change\n                if (event.data.self.dataToDeal !== null) {\n                    // Add to deal array if only the plugin is editing mode and line\n                    // display mode is dynamic\n                    if (event.data.self.settings.lineDisplayMode > 1) {\n                        if ($.inArray(data.id, event.data.self.dataToDeal) < 0) {\n                            event.data.self.dataToDeal.push(data.id);\n                        }\n                    }\n                }\n            }\n        },\n        /**\n         * Fired on end data change event\n         * @method onEndDataChange\n         * @param {Object} event\n         */\n        onEndDataChange: function (event) {\n            event.data.self.loadDataStarted = false;\n\n            event.data.self.updateBlocks(event.data.self.dataToDeal);\n            event.data.self.dataToDeal = [];\n            //update line plugin size\n            if (event.data.self.settings.displayLines === false) {\n                event.data.self.updateComponentsLineHeight();\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"EndDataChange\");\n            }\n        },\n        /**\n         * Fired on zoom range change\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onZoomRangeChange: function (event, data) {\n            if (data.eventTag === event.data.self.eventTag) {\n                return;\n            }\n            if (Math.ceil(event.data.self.zTcin) !== Math.ceil(data.zTcin) || Math.ceil(event.data.self.zTcout) !== Math.ceil(data.zTcout)) {\n                event.data.self.updateZoomChange(data.zTcin, data.zTcout);\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onZoomRangeChange zTcin:\" + data.zTcin + \" zTcout\" + data.zTcout);\n                }\n            }\n        },\n        /**\n         * Fired for select metadata change\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectedMetadataChange: function (event, data) {\n            event.data.self.componentsContainer.find('.component').removeClass('activated');\n            if (data.metadataId !== null) {\n                event.data.self.componentsContainer.find('.component[data-metadata-id=\"' + data.metadataId + '\"]').addClass('activated');\n                var movePos = event.data.self.componentsContainer.find('.component[data-metadata-id=\"' + data.metadataId + '\"]:first').position();\n                var scrollTop = event.data.self.componentsContainer.get(0).scrollTop;\n                if (typeof movePos === 'object' && movePos.hasOwnProperty('top')) {\n                    scrollTop = Math.min(scrollTop, event.data.self.componentsContainer.get(0).scrollHeight);\n                    event.data.self.componentsContainer.stop().animate({\n                        scrollTop: scrollTop + movePos.top\n                    }, 500, 'easeInOutExpo');\n                }\n            }\n        },\n        /**\n         * Fired on cue point data change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onCuepointDataChange: function (event, data) {\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: data.id\n            });\n        },\n        /**\n         * Fired on segment data change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSegmentDataChange: function (event, data) {\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: data.id\n            });\n        },\n        /**\n         * Fired on visual component data change event\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onVisualComponentDataChange: function (event, data) {\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.DATA_CHANGE, {\n                id: data.id\n            });\n        },\n        /**\n         * Fired for select line\n         * @param {Object} event\n         */\n        onSelectComponents: function (event) {\n            var metadataId = $(event.currentTarget).attr('data-metadata-id');\n            event.data.self.mediaPlayer.setSelectedMetadataId(metadataId);\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onSelectComponents  : \" + metadataId);\n            }\n        },\n        /**\n         * Fired on scroll up event\n         * @param {Object} event\n         */\n        onScrollUp: function (event) {\n            var moveHeight = event.data.self.componentsContainer.find('.component').height();\n            var scrollTop = event.data.self.componentsContainer.get(0).scrollTop - moveHeight;\n            scrollTop = Math.min(scrollTop, event.data.self.componentsContainer.get(0).scrollHeight);\n            event.data.self.componentsContainer.stop().animate({\n                    scrollTop: scrollTop\n                },\n                500, 'easeInOutExpo');\n        },\n        /**\n         * Fired on scroll down event\n         * @param {Object} event\n         */\n        onScrollDown: function (event) {\n            var moveHeight = event.data.self.componentsContainer.find('.component').height();\n            var scrollTop = event.data.self.componentsContainer.get(0).scrollTop + moveHeight;\n            scrollTop = Math.min(scrollTop, event.data.self.componentsContainer.get(0).scrollHeight);\n            event.data.self.componentsContainer.stop().animate({\n                scrollTop: scrollTop\n            }, 500, 'easeInOutExpo');\n        },\n        /**\n         * Fired on Mousewheel event\n         * @param {Object} event\n         */\n        onComponentsMousewheel: function (event) {\n            // preventDefault scrool page event\n            event.preventDefault();\n            var delta = Math.max(-1, Math.min(1, (event.originalEvent.wheelDelta || -event.originalEvent.detail)));\n            if (typeof delta === \"number\") {\n                var scrollTop = event.data.self.componentsContainer.get(0).scrollTop - delta * event.data.self.settings.mouseWheelCoef;\n                scrollTop = Math.min(scrollTop, event.data.self.componentsContainer.get(0).scrollHeight);\n                event.data.self.componentsContainer.get(0).scrollTop = scrollTop;\n            }\n        },\n        /**\n         * On click to select item\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onSelectItem: function (event, data) {\n            var metadataId = $(event.target).attr('data-metadata-id');\n            if (event.data.self.mediaPlayer.getSelectedMetadataId() !== metadataId) {\n                event.data.self.mediaPlayer.setSelectedMetadataId(metadataId);\n            }\n            event.data.self.mediaPlayer.addSelectedItem(data.metadata);\n        },\n        /**\n         * Remove select item\n         * @param event\n         * @param data\n         */\n        onRemoveSelectItem: function (event, data) {\n            if (data.metadata !== null && typeof data.metadata === \"object\") {\n                data.metadata.selected = false;\n            }\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onRemoveSelectItem \", data);\n            }\n        },\n        /**\n         * Fired on zoom zone change\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onZoomZoneChangeWithFocusComponent: function (event, data) {\n            if (Math.ceil(event.data.self.zTcin) !== Math.ceil(data.zTcin) || Math.ceil(event.data.self.zTcout) !== Math.ceil(data.zTcout)) {\n                event.data.self.updateZoomChange(data.zTcin, data.zTcout);\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onZoomZoneChangeWithFocusComponent zTcin:\" + data.zTcin + \" zTcout\" + data.zTcout);\n                }\n            }\n        },\n        /**\n         * Fired on resize start event\n         * @param {Object} evnet\n         * @param {Object} ui\n         */\n        onResizeStart: function (evnet, ui) {\n            var element = ui.element;\n            var lineHeight = $(element).find('.component').first().height();\n            var linesOffset = 2 * $(element).find('.component').length;\n            var headerAndFooterHeight = parseFloat($(element).find('.timeaxis').height() + $(element).find('.module-nav-bar-container').height());\n            element.resizable(\"option\", \"minHeight\", lineHeight + headerAndFooterHeight);\n            element.resizable(\"option\", \"maxHeight\", (lineHeight * $(element).find('.component').length - linesOffset) + headerAndFooterHeight);\n        },\n        /**\n         * Fired on resize event\n         * @param {Object} event\n         * @param {Object} ui\n         */\n        onResize: function (event, ui) {\n            var element = ui.element;\n            var headerAndFooterHeight = parseFloat($(element).find('.timeaxis').height() + $(element).find('.module-nav-bar-container').height());\n            $(element).find('.components').first().css('height', ui.size.height - headerAndFooterHeight);\n        },\n        /**\n         * Fired on visual component bind metadata\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onVisualComponentBindMetadata: function (event, data) {\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.BIND_METADATA, {\n                id: data.id\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onVisualComponentBindMetadata  : \" + data.id);\n            }\n        },\n        /**\n         * Fired on visual component un bind metadata\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onVisualComponentUnBindMetadata: function (event, data) {\n            event.data.self.mediaPlayer.getContainer().trigger(fr.ina.amalia.player.PlayerEventType.UNBIND_METADATA, {\n                id: data.id\n            });\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onVisualComponentUnBindMetadata  : \" + data.id);\n            }\n        },\n        /**\n         * Fired on click to nav control\n         * @param {Object} event\n         * @param {Object} data\n         */\n        onClickNavControls: function (event, data) {\n            if (data.hasOwnProperty('type') === true) {\n                var tc = (data.type === 'next') ? event.data.component.getNextItem(event.data.self.mediaPlayer.getCurrentTime()) : event.data.component.getPrevItem(event.data.self.mediaPlayer.getCurrentTime());\n                event.data.self.mediaPlayer.pause();\n                if (tc !== null) {\n                    event.data.self.mediaPlayer.setCurrentTime(tc);\n                }\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/visual-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to handle spatials component\n * @class VisualComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @extends fr.ina.amalia.player.plugins.timeline.BaseComponent\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.timeline.BaseComponent(\"fr.ina.amalia.player.plugins.timeline.VisualComponent\", {\n        ComponentClassCss: \"visual-component\",\n        ComponentModuleClassCss: \"module-visual\",\n        COMPONENT_NAME: 'visual',\n        STYLE_CLASSNAME_BIND_ON: 'ajs-icon-eye-on',\n        STYLE_CLASSNAME_BIND_OFF: 'ajs-icon-eye-off',\n        eventTypes: {\n            DATA_CHANGE: \"fr.ina.amalia.player.plugins.timeline.VisualComponent.eventTypes.DATA_CHANGE\",\n            BIND: \"fr.ina.amalia.player.plugins.timeline.VisualComponent.eventTypes.BIND\",\n            UNBIND: \"fr.ina.amalia.player.plugins.timeline.VisualComponent.eventTypes.UNBIND\"\n        }\n    },\n    {\n\n        initialize: function () {\n            this._super();\n\n            this.mainContainer.on('click', '.cuepoint', {\n                self: this\n            }, this.onClickAtCuepoint);\n        },\n        createToolBar: function () {\n            var toolbarContainer = this._super();\n            // expend bouton\n            var bindBtn = this.createButton(fr.ina.amalia.player.PlayerMessage.PLUGIN_TIMELINE_LABEL_BIND, \"bind-btn\");\n            bindBtn.on('click', {\n                self: this\n            }, this.onClickBindBtn);\n            bindBtn.addClass(this.Class.STYLE_CLASSNAME_BIND_ON);\n            toolbarContainer.append(bindBtn);\n        },\n        /**\n         * In charge to add item\n         * @method addItem\n         * @param {Object} data\n         */\n        addItem: function (data) {\n            var _itemContainer = null;\n            var _selectedData = (data.hasOwnProperty('selected') && data.selected === true);\n            var _title = (data.hasOwnProperty('label') === true && data.label !== '' && data.label !== null) ? data.label : '';\n            var _lineContent = this.mainContainer.find('.line-content').first();\n            // global tc\n            var _gtc = this.tcout - this.tcin;\n            if (data.hasOwnProperty('tcin') && data.hasOwnProperty('tcout') && data.hasOwnProperty('sublocalisations') === true && data.sublocalisations !== null && data.sublocalisations.hasOwnProperty('localisation')) {\n                this.createSublocalisationSegments(_lineContent, data, _title, _selectedData);\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"addItem segment tcin: \" + data.tcin + \" tcout: \" + data.tcout);\n                }\n            }\n            else if (data.hasOwnProperty('tc')) {\n                var _tc = parseFloat(data.tc);\n                var _percentPos = ((_tc - this.tcin) * 100) / _gtc;\n                _title = (_title !== \"\") ? _title : 'Tc : ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(_tc, this.settings.framerate, this.settings.timeFormat);\n                if (_tc >= this.tcin && _tc <= this.tcout) {\n                    _itemContainer = this.createCuePointElement(_tc, _percentPos, _title, data.tclevel);\n                    _itemContainer.data('metadata', data);\n                    if (this.settings.editable === true) {\n                        // draggable\n                        _itemContainer.draggable({\n                            axis: \"x\",\n                            drag: function (event, ui) {\n                                var targetElement = $(event.target);\n                                var parentElement = targetElement.parent();\n                                var newLeft = Math.max(0, ui.position.left);\n                                ui.position.left = Math.min(parentElement.first().width(), newLeft);\n                            }\n                        });\n                        _itemContainer.on(\"dragstop\", {\n                                self: this\n                            },\n                            this.onCuepointDragStop);\n                        _itemContainer.css(\"position\", \"absolute\");\n\n                    }\n                    if (_selectedData) {\n                        _itemContainer.addClass('selected');\n                        if (data.hasOwnProperty('formCreated') && data.formCreated === false) {\n                            this.mainContainer.trigger(this.Class.CLICK_SELECT, {\n                                tc: _tc,\n                                metadata: data\n                            });\n                        }\n                    }\n                    _lineContent.append(_itemContainer);\n                }\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"addItem cuepoint tcin:  tc:\" + data.tc);\n                }\n            }\n        },\n        createSublocalisationSegments: function (lineContent, data, title) {\n            this.localisationManager.setLoc(data.sublocalisations.localisation);\n            var locTc = this.localisationManager.getLocTc();\n            if (locTc !== null) {\n                //update main tc\n                data.tcin = locTc.tcin;\n                data.tcout = locTc.tcout;\n                var _startTc = parseFloat(locTc.tcin);\n                var _endTc = parseFloat(locTc.tcout);\n                var localisation = data.sublocalisations.localisation;\n                var _gtc = this.tcout - this.tcin;\n                var percentWidth = ((_endTc - _startTc) * 100) / _gtc;\n                var percentLeft = ((_startTc - this.tcin) * 100) / _gtc;\n                var color = (this.settings.color !== \"\") ? 'color:' + this.settings.color + '; background-color:' + this.settings.color + ';' : '';\n                var container = $('<div>', {\n                    class: 'item segment marker',\n                    style: 'left: ' + percentLeft + '%; width:' + percentWidth + '%;' + color,\n                    title: 'Tc in: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(_startTc, this.settings.framerate, this.settings.timeFormat) + '\\n Tc out: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(_endTc, this.settings.framerate, this.settings.timeFormat),\n                    'data-tc': _startTc,\n                    'data-tcin': _startTc,\n                    'data-tcout': _endTc\n                });\n                lineContent.append(container);\n                container.data('metadata', data);\n\n                var _itemContainer = null;\n                for (var i = 0;\n                     i < localisation.length;\n                     i++) {\n                    var _tc = localisation[i].tc;\n                    var _percentPos = ((_tc - _startTc) * 100) / (_endTc - _startTc);\n                    title = (title !== \"\") ? title : 'Tc: ' + fr.ina.amalia.player.helpers.UtilitiesHelper.formatTime(_tc, this.settings.framerate, this.settings.timeFormat);\n                    _itemContainer = this.createCuePointElement(_tc, _percentPos, title);\n                    _itemContainer.data('metadata', localisation[i]);\n                    _itemContainer.draggable({\n                        axis: \"x\",\n                        drag: this.onItemDrag\n                    });\n                    _itemContainer.on(\"dragstop\", {\n                            self: this\n                        },\n                        this.onSegmentDragStop);\n                    container.append(_itemContainer);\n                }\n\n                if (percentWidth < 100) {\n                    container.draggable({\n                        axis: \"x\",\n                        drag: function (event, ui) {\n                            var targetElement = $(event.target);\n                            var parentElement = targetElement.parent();\n                            var newLeft = Math.max(0, ui.position.left);\n                            ui.position.left = Math.min(parentElement.first().width() - targetElement.width(), newLeft);\n                        }\n                    });\n                    container.on(\"dragstop\", {\n                            self: this\n                        },\n                        this.onShiftDragStop);\n                }\n            }\n        },\n        /**\n         * In charge to crate the cue point\n         * @param {Number} tc time code\n         * @param {Number} percentPos\n         * @param {String} title\n         * @param {Number} level\n         * @return {Object} element\n         */\n        createCuePointElement: function (tc, percentPos, title, level) {\n            var color = (typeof this.settings.color !== \"undefined\" && this.settings.color !== \"\") ? this.settings.color : '#3cf';\n            var icon = (typeof this.settings.icon !== \"undefined\" && this.settings.icon !== \"\") ? this.settings.icon : 'circle';\n            var container = $('<i>', {\n                'class': 'item cuepoint ajs-icon ajs-icon-' + icon,\n                'style': 'left: ' + percentPos + '%; color:' + color + ';',\n                'title': title,\n                'data-tc': tc,\n                'data-tclevel': level\n            });\n\n            return container;\n        },\n        /**\n         * In charge to remove items\n         * @method removeItems\n         */\n        removeItems: function () {\n            var lineContent = this.mainContainer.find('.line-content').first();\n            lineContent.find('.item').remove();\n        },\n        /**\n         * Fired on click event at the cue point\n         * @method onClickAtSegment\n         * @param {Object} event\n         * @event fr.ina.amalia.player.components.CuepointsComponent.eventTypes.CLICK\n         */\n        onClickAtCuepoint: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tc = parseFloat(currentTarget.data('tc'));\n            var data = $(event.currentTarget).data('metadata');\n            // Alt+Click\n            if (event.altKey && data.selected !== true && event.data.self.settings.editable === true) {\n                currentTarget.addClass('selected');\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_SELECT, {\n                    tc: tc,\n                    metadata: data\n                });\n            }\n            else {\n                event.data.self.mainContainer.trigger(event.data.self.Class.CLICK_TC, {\n                    tc: tc\n                });\n            }\n\n            if (event.data.self.logger !== null) {\n                event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickAtCuepoint tc:\" + tc);\n            }\n        },\n        /**\n         * Fired on drag stop event\n         * @method onDragStop\n         * @param {Object} event\n         */\n        onCuepointDragStop: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tc = parseFloat(((event.data.self.tcout - event.data.self.tcin) * currentTarget.position().left) / event.data.self.mainContainer.first().width());\n            $(event.currentTarget).data('metadata').tc = Math.max(0, tc);\n            $(event.currentTarget).data('metadata').tcin = Math.max(0, tc);\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.DATA_CHANGE, {\n                id: event.data.self.getMetadataId()\n            });\n        },\n        /**\n         * Fired on drag stop event\n         * @method onDragStop\n         * @param {Object} event\n         */\n        onSegmentDragStop: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tc = parseFloat(((event.data.self.tcout - event.data.self.tcin) * (currentTarget.parent().position().left + currentTarget.position().left)) / event.data.self.mainContainer.first().width()) + event.data.self.tcin;\n            //Set Tc\n            $(event.currentTarget).data('metadata').tc = Math.max(0, tc);\n\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.DATA_CHANGE, {\n                id: event.data.self.getMetadataId()\n            });\n        },\n        /**\n         * Fired on drag stop event\n         * @method onShiftDragStop\n         * @param {Object} event\n         */\n        onShiftDragStop: function (event) {\n            var currentTarget = $(event.currentTarget);\n            var tcin = parseFloat(((event.data.self.tcout - event.data.self.tcin) * (currentTarget.parent().position().left + currentTarget.position().left)) / event.data.self.mainContainer.first().width()) + event.data.self.tcin;\n            //Shift tcin\n            event.data.self.localisationManager.shiftSpacialLocBlock(currentTarget.data('metadata'), tcin);\n\n            event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.DATA_CHANGE, {\n                id: event.data.self.getMetadataId()\n            });\n        },\n        /**\n         * Fired on click to bind button\n         * @method onClickBindBtn\n         * @param {Object} event\n         */\n        onClickBindBtn: function (event) {\n            var currentTarget = $(event.currentTarget);\n            if (currentTarget.hasClass(event.data.self.Class.STYLE_CLASSNAME_BIND_ON)) {\n                currentTarget.removeClass(event.data.self.Class.STYLE_CLASSNAME_BIND_ON).addClass(event.data.self.Class.STYLE_CLASSNAME_BIND_OFF);\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickBindBtn UNBIND:\" + event.data.self.getMetadataId());\n                }\n                event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.UNBIND, {\n                    id: event.data.self.getMetadataId()\n                });\n            }\n            else if (currentTarget.hasClass(event.data.self.Class.STYLE_CLASSNAME_BIND_OFF)) {\n                currentTarget.removeClass(event.data.self.Class.STYLE_CLASSNAME_BIND_OFF).addClass(event.data.self.Class.STYLE_CLASSNAME_BIND_ON);\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onClickBindBtn BIND:\" + event.data.self.getMetadataId());\n                }\n                event.data.self.mainContainer.trigger(event.data.self.Class.eventTypes.BIND, {\n                    id: event.data.self.getMetadataId()\n                });\n            }\n        },\n        onItemDrag: function (event, ui) {\n            var targetElement = $(event.target);\n            var parentElement = targetElement.parent();\n            var newLeft = ui.position.left;\n            var metadata = targetElement.data('metadata');\n            if (metadata.hasOwnProperty('firstItem') && metadata.firstItem === true) {\n                newLeft = Math.max(-parentElement.first().offset().left + targetElement.width(), ui.position.left);\n                ui.position.left = Math.min(parentElement.first().width(), newLeft);\n            }\n            else if (metadata.hasOwnProperty('lastItem') && metadata.lastItem === true) {\n                var maxLeftPos = parentElement.first().parent().width() - parentElement.first().offset().left + targetElement.width();\n                newLeft = Math.max(0, newLeft);\n                ui.position.left = Math.min(newLeft, maxLeftPos);\n            }\n            else {\n                newLeft = Math.max(0, newLeft);\n                ui.position.left = Math.min(parentElement.first().width(), newLeft);\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/timeline/zoom-component.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to zoom on the time axe component\n * @class ZoomComponent\n * @namespace fr.ina.amalia.player.plugins.timeline\n * @module plugin\n * @submodule plugin-timeline\n * @constructor\n * @param {Object} settings\n * @param {Object} container\n */\n$.Class(\"fr.ina.amalia.player.plugins.timeline.ZoomComponent\", {\n        styleCss: 'display: block; position: absolute;height: 100px;width: 1px;background: aqua;margin-top: -50px;',\n        eventTypes: {\n            CHANGE: \"fr.ina.amalia.player.plugins.timeline.ZoomComponent.event.change\"\n        }\n    },\n    {\n        /**\n         * Instance of logger\n         * @property logger\n         * @type {Object}\n         * @default null\n         */\n        logger: null,\n        /**\n         * Main container\n         * @property container\n         * @type {Object}\n         * @default null\n         */\n        container: null,\n        /**\n         * Configuration\n         * @property settings\n         * @type {Object}\n         * @default \"{}\"\n         */\n        settings: {},\n        /**\n         * Tc offset\n         * @property tcOffset\n         * @type {Number}\n         * @default 0\n         */\n        tcOffset: 0,\n        /**\n         * Media duration\n         * @property duration\n         * @type {Object}\n         * @default 0\n         */\n        duration: 0,\n        /**\n         * Start time code\n         * @property currentTcin\n         * @type {Number}\n         * @default 0\n         */\n        currentTcin: 0,\n        /**\n         * End time code\n         * @property currentTcout\n         * @type {Number}\n         * @default 0\n         */\n        currentTcout: 0,\n        /**\n         * Slide speed\n         * @property slideSpeed\n         * @type {Number}\n         * @default 0\n         */\n        slideSpeed: 0,\n        /**\n         * Zoom speed\n         * @property zoomSpeed\n         * @type {Number}\n         * @default 2\n         */\n        zoomSpeed: 2,\n        /**\n         * Init this class\n         * @constuctor\n         * @param {Object} container\n         * @param {Object} duration\n         * @param {Object} settings\n         */\n        init: function (container, duration, settings) {\n            this.settings = $.extend({\n                    'debug': false,\n                    'zoomSpeed': 5,\n                    'tcOffset': 0\n                },\n                settings || {});\n            if (typeof fr.ina.amalia.player.log !== \"undefined\" && typeof fr.ina.amalia.player.log.LogHandler !== \"undefined\") {\n                this.logger = new fr.ina.amalia.player.log.LogHandler({\n                    enabled: this.settings.debug\n                });\n            }\n            this.container = container;\n            this.tcOffset = parseFloat(this.settings.tcOffset) * 1000;\n            this.duration = duration * 1000;\n            this.zoomSpeed = this.settings.zoomSpeed;\n            this.slideSpeed = 0;\n            this.initialize(this.tcOffset, this.duration + this.tcOffset);\n        },\n        /**\n         * Initialize\n         * @method initialize\n         * @param {Number} tcin\n         * @param {Number} tcout\n         */\n        initialize: function (tcin, tcout) {\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"Initialize  tcin\" + tcin + \" tcout:\" + tcout + \" duration : \" + this.duration);\n            }\n            this.container.append($('<span>', {\n                'class': 'time',\n                'style': this.Class.styleCss\n            }));\n\n            var list = $('<div>', {\n                'class': 'list'\n            });\n            this.container.append(list);\n            this.setTc(tcin, tcout);\n        },\n        /**\n         * Set time code\n         * @method setTc\n         * @param tcin\n         * @param tcout\n         */\n        setTc: function (tcin, tcout) {\n            if (typeof tcin === \"number\" && typeof tcout === \"number\" && isFinite(tcin) && isFinite(tcout)) {\n                this.currentTcin = tcin;\n                this.currentTcout = tcout;\n                this.updateTime();\n            }\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"setTc / currentTcin :\" + this.currentTcin + \" currentTcout:\" + this.currentTcout);\n            }\n        },\n        /**\n         * update time\n         * @method updateTime\n         */\n        updateTime: function () {\n            this.currentTcin = Math.max(this.tcOffset, this.currentTcin);\n            this.currentTcout = Math.min(this.currentTcout, this.duration + this.currentTcout);\n            this.updateTc();\n            this.sendEvent();\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"updateTime currentTcin :\" + this.currentTcin + \" currentTcout:\" + this.currentTcout);\n            }\n        },\n        /**\n         * Trigger event zoom change event\n         * @method sendEvent\n         */\n        sendEvent: function () {\n            this.container.trigger(this.Class.eventTypes.CHANGE, {\n                'tcin': this.currentTcin,\n                'tcout': this.currentTcout\n            });\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"sendEvent sendEvent :\" + this.Class.eventTypes.CHANGE + \" currentTcin:\" + this.currentTcin + \" currentTcout \" + this.currentTcout);\n            }\n        },\n        /**\n         * Update time code\n         * @method updateTc\n         */\n        updateTc: function () {\n            this.container.attr({\n                'data-tcin': this.currentTcin,\n                'data-tcout': this.currentTcout\n            });\n        },\n        /**\n         * Handle zoom in\n         * @method zoomIn\n         */\n        zoomIn: function (tc) {\n            var duration = (this.currentTcout - this.currentTcin);\n            var coef = Math.round(duration / this.zoomSpeed);\n            if (coef !== 0) {\n                this.slideSpeed = coef;\n                this.currentTcin = tc - coef;\n                this.currentTcout = tc + coef;\n                this.updateTime();\n            }\n        },\n        /**\n         * Handle zoom out\n         * @method zoomOut\n         */\n        zoomOut: function () {\n            var duration = (this.currentTcout - this.currentTcin);\n            var coef = Math.round(duration / this.zoomSpeed);\n            coef = (coef <= 0) ? 1 : coef;\n            this.slideSpeed = coef;\n            this.currentTcin -= coef;\n            this.currentTcout += coef;\n            this.updateTime();\n        },\n        /**\n         * Handle slide left\n         * @method slideLeft\n         */\n        slideLeft: function () {\n            if ((this.currentTcin - this.slideSpeed) >= 0) {\n                this.currentTcin = this.currentTcin - this.slideSpeed;\n                this.currentTcout = this.currentTcout - this.slideSpeed;\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"slideLeft currentZoom : \" + this.currentZoom + \" currentTcin :\" + this.currentTcin + \" currentTcout:\" + this.currentTcout);\n                }\n                this.updateTc();\n                this.sendEvent();\n            }\n\n        },\n        /**\n         * Handle slide right\n         * @method slideRight\n         */\n        slideRight: function () {\n            if ((this.currentTcout + this.slideSpeed) <= this.duration) {\n                this.currentTcin = this.currentTcin + this.slideSpeed;\n                this.currentTcout = this.currentTcout + this.slideSpeed;\n                if (this.logger !== null) {\n                    this.logger.trace(this.Class.fullName, \"slideRight currentZoom : \" + this.currentZoom + \" currentTcin :\" + this.currentTcin + \" currentTcout:\" + this.currentTcout);\n                }\n                this.updateTc();\n                this.sendEvent();\n            }\n        }\n    });\n"
  },
  {
    "path": "src/plugins/watermark/watermark.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge to watermark plugin\n * @class WatermarkPlugin\n * @namespace fr.ina.amalia.player.plugins\n * @module plugin\n * @submodule plugin-watermark\n * @constructor\n * @extends fr.ina.amalia.player.plugins.PluginBase\n * @param {Object} settings\n * @param {Object} container\n */\nfr.ina.amalia.player.plugins.PluginBase.extend(\"fr.ina.amalia.player.plugins.WatermarkPlugin\", {\n        classCss: \"ajs-plugin plugin-watermark\",\n        eventTypes: {}\n    },\n    {\n        /**\n         * Main container of this plugin\n         * @property container\n         * @type {Object}\n         * @default []\n         */\n        container: null,\n        /**\n         * Ratio of disply object\n         * @property ratio\n         * @type {Object}\n         * @default []\n         */\n        ratio: 0,\n        /**\n         * Object position\n         * @property placement\n         * @type {Object}\n         * @default []\n         */\n        placement: '',\n        /**\n         * List of positions\n         * @property placementList\n         * @type {Object}\n         * @default []\n         */\n        placementList: [\n            // Center\n            'c',\n            // Top left\n            'tl',\n            // TopRight\n            'tr',\n            // BottomLeft\n            'bl',\n            // BottomRight\n            'br'\n        ],\n        /**\n         * Initialize watermark plugin and create container of this plugin\n         * @method initialize\n         */\n        initialize: function () {\n            this._super();\n            this.settings = $.extend({\n                backgroundImageUrl: '',\n                ratio: 50,\n                placement: 'c',\n                imageWidth: 0,\n                imageHeight: 0,\n                topOffet: 45\n            }, this.settings.parameters || {});\n\n            if (this.settings.backgroundImageUrl !== '') {\n                this.setPlacement(this.settings.placement);\n                this.container = $('<div>', {\n                    'class': this.Class.classCss\n                });\n                this.pluginContainer.append(this.container);\n                this.defineListeners();\n            }\n        },\n        /**\n         * Set player events\n         * @method defineListeners\n         */\n        defineListeners: function () {\n            var mainContainer = this.mediaPlayer.getContainer();\n            // Player events\n            mainContainer.one(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this\n            }, this.onPluginReady);\n            // call function 200 ms after resize is complete.\n            $(window).on('debouncedresize', {\n                self: this\n            }, this.onWindowResize);\n            if (this.logger !== null) {\n                this.logger.trace(this.Class.fullName, \"definePlayerListeners\");\n            }\n        },\n        /**\n         * Initialize watermark plugin on load start event and create plugin\n         * container\n         * @method initializeOnLoadStart\n         * @returns {undefined}\n         */\n        initializeOnLoadStart: function () {\n            this.createWatermarkElement();\n        },\n        /**\n         * In charge to create watermark element\n         * @method createWatermarkElement\n         */\n        createWatermarkElement: function () {\n            var watermark = $('<div>', {\n                style: 'background-image: url(' + this.settings.backgroundImageUrl + ');',\n                'class': 'watermark'\n            });\n            this.container.append(watermark);\n            this.updatePos();\n        },\n        /**\n         * In charge of update positions\n         * @method updatePos\n         */\n        updatePos: function () {\n            var videoSize = this.getVideoSize();\n            var width = videoSize.w;\n            var height = videoSize.h;\n            var left = (this.container.width() - width) / 2;\n            var top = (this.container.height() - height - this.settings.topOffet) / 2;\n            this.container.find('.watermark').css({\n                width: width + 'px',\n                height: height + 'px',\n                top: top + 'px',\n                left: left + 'px'\n            });\n            this.updateImagePositionWithPlacement();\n            this.updateImageSize();\n        },\n        /**\n         * Set image size\n         * @method updateImageSize\n         */\n        updateImageSize: function () {\n            var videoSize = this.getVideoSize();\n            var rw = videoSize.w / this.settings.imageWidth;\n            var rh = videoSize.h / this.settings.imageHeight;\n            var ratio = Math.min(rw, rh);\n            ratio = ratio * (this.settings.ratio / 100);\n            var w = ratio * this.settings.imageWidth;\n            var h = ratio * this.settings.imageHeight;\n            var watermarkElement = this.container.find('.watermark');\n            watermarkElement.css('background-size', w + 'px ' + h + 'px');\n        },\n        /**\n         * In charge of set image position with specified configuration\n         * @method updateImagePositionWithPlacement\n         */\n        updateImagePositionWithPlacement: function () {\n            var watermarkElement = this.container.find('.watermark');\n            // Default Center\n            var x = '50%';\n            var y = '50%';\n            if (this.placement === 'tl') {\n                // Top left\n                x = '0%';\n                y = '0%';\n            }\n            else if (this.placement === 'tr') {\n                // TopRight\n                x = '100%';\n                y = '0%';\n            }\n            else if (this.placement === 'bl') {\n                // BottomLeft\n                x = '0%';\n                y = '100%';\n            }\n            else if (this.placement === 'br') {\n                // BottomRight\n                x = '100%';\n                y = '100%';\n            }\n\n            watermarkElement.css('background-position', x + ' ' + y);\n        },\n        /**\n         * Return video size\n         * @method getVideoSize\n         * @return {Object}\n         */\n        getVideoSize: function () {\n            var player = this.mediaPlayer.getMediaPlayer();\n            var videoHeight = player.get(0).videoHeight;\n            var videoWidth = player.get(0).videoWidth;\n            if (videoHeight === 0) {\n                videoHeight = player.height();\n            }\n            if (videoWidth === 0) {\n                videoWidth = player.width();\n            }\n            var widthRatio = player.width() / videoWidth;\n            var heightRatio = player.height() / videoHeight;\n            var ratio = Math.min(widthRatio, heightRatio);\n            return {\n                w: ratio * videoWidth,\n                h: ratio * videoHeight\n            };\n        },\n        /**\n         * Return image position\n         * @method getPosition\n         * @return {String}\n         */\n        getPosition: function () {\n            return this.placement;\n        },\n        /**\n         * Set image position$\n         * @method setPlacement\n         * @param {String} placement\n         */\n        setPlacement: function (placement) {\n            if ($.inArray(placement, this.placementList) === -1) {\n                this.placement = this.placementList[0].toString();\n            }\n            else {\n                this.placement = placement.toString();\n            }\n        },\n        /** Player Events * */\n        /**\n         * Fired when plugin ready event\n         * @method onPluginReady\n         * @param {Object} event\n         */\n        onPluginReady: function (event) {\n            event.data.self.initializeOnLoadStart();\n        },\n        /**\n         * Fired on windows resize event\n         * @method onWindowResize\n         * @param {Object} event\n         */\n        onWindowResize: function (event) {\n            event.data.self.updatePos();\n        }\n    });\n"
  },
  {
    "path": "src/plugins/yt-player/yt-player.js",
    "content": "/**\n * Copyright (c) 2015-2023 Institut National de l'Audiovisuel, INA\n *\n * This file is part of amalia.js\n *\n * Amalia.js is free software: you can redistribute it and/or modify it under\n * the terms of the MIT License\n *\n * Redistributions of source code, javascript and css minified versions must\n * retain the above copyright notice, this list of conditions and the following\n * disclaimer\n *\n * Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission\n *\n * You should have received a copy of the MIT License along with\n * amalia.js. If not, see <https://opensource.org/license/mit/>\n *\n * Amalia.js is distributed in the hope that it will be useful, but WITHOUT ANY\n * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n * A PARTICULAR PURPOSE.\n */\n/**\n * In charge of the player html5\n * @class YtPlayer\n * @module player\n * @namespace fr.ina.amalia.player\n * @constructor\n * @param {Object} settings\n * @param {Object} mediaContainer\n */\nfr.ina.amalia.player.BasePlayer.extend(\"fr.ina.amalia.player.YtPlayer\", {\n        ytPlayerContainerClassCss: \"player\",\n        ytPlayerContainerStyle: \"position: relative; width: inherit; height: inherit; background-color: black; \",\n        YT_BASE_URI: \"https://www.youtube.com/iframe_api\"\n    },\n    {\n        /**\n         * Youtube player container\n         * @property ytPlayer\n         * @type {Object}\n         * @default null\n         */\n        ytPlayerContainer: null,\n        /**\n         * Youtube player\n         * @property ytPlayer\n         * @type {Object}\n         * @default null\n         */\n        ytPlayer: null,\n        /**\n         * last state change event for onPlayerStateChange function\n         * @property ytPlayer\n         * @type {Object}\n         * @default null\n         */\n        lastState: null,\n        /**\n         * youtybe media time update\n         * @property ytTimeupdateInterval\n         * @type {Object}\n         * @default null\n         */\n        ytTimeupdateInterval: null,\n\n        /**\n         * Method in charge to initialize youtube player\n         * @method initialize\n         * @override\n         */\n        initialize: function () {\n            this.ytTimeupdateInterval = null;\n            this.ytPlayerContainer = $('<div/>', {\n                'class': this.Class.ytPlayerContainerClassCss,\n                'style': this.Class.ytPlayerContainerStyle,\n                'id': fr.ina.amalia.player.helpers.UtilitiesHelper.generateUUID('ajs-yt-player')\n            });\n            this.mediaContainer.append(this.ytPlayerContainer);\n            this.setSrc(this.settings.src, this.settings.autoplay);\n            this._super();\n            $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', {\n                self: this\n            }, this.onFullscreenHandler);\n        },\n        /**\n         * In charge to set source with autoplay state\n         * @param {String} src\n         * @param {Boolean} autoplay\n         * @method setSrc\n         */\n        setSrc: function (src, autoplay) {\n            var withControlBarYt = (typeof this.settings.controlBar === \"object\" && this.settings.controlBar.hasOwnProperty('hide') === true && this.settings.controlBar.hide === true);\n            this.ytPlayer = new YT.Player(this.ytPlayerContainer.attr('id'), {\n                videoId: src,\n                playerVars: {\n                    'autoplay': autoplay, showinfo: 0, controls: (withControlBarYt === true) ? 1 : 0, rel: 0, showsearch: 0, iv_load_policy: 3\n                },\n                events: {\n                    'onReady': $.proxy(this.onPlayerReady, this),\n                    'onStateChange': $.proxy(this.onPlayerStateChange, this),\n                    //'onPlaybackQualityChange': onPlayerPlaybackQualityChange,\n                    'onError': $.proxy(this.onPlayerError, this)\n                }\n            })\n            ;\n        },\n        /**\n         * In charge to update container size\n         */\n        updateContainerSize: function () {\n            var stickyMode = !(typeof this.settings.controlBar === \"object\" && this.settings.controlBar.hasOwnProperty('sticky') === true && this.settings.controlBar.sticky === true);\n            if (!stickyMode) {\n                var controlBarHeight = (typeof this.settings.controlBar === \"object\" && this.settings.controlBar.hasOwnProperty('height') === true && this.settings.controlBar.height !== \"\") ? this.settings.controlBar.height : 45;\n                this.mediaContainer.find('iframe').first().css('height', this.mediaContainer.height() - controlBarHeight);\n            }\n        },\n        onPlayerReady: function (event) {\n            this.hideLoader();\n            var stickyMode = !(typeof this.settings.controlBar === \"object\" && this.settings.controlBar.hasOwnProperty('sticky') === true && this.settings.controlBar.sticky === true);\n            if (!stickyMode) {\n                var controlBarHeight = (typeof this.settings.controlBar === \"object\" && this.settings.controlBar.hasOwnProperty('height') === true && this.settings.controlBar.height !== \"\") ? this.settings.controlBar.height : 45;\n                this.mediaContainer.find('iframe').first().css('height', this.mediaContainer.height() - controlBarHeight);\n            }\n            event.target.setVolume((this.localStorageManager.hasItem('volume') === false) ? this.settings.defaultVolume : this.localStorageManager.getItem('volume'));\n        },\n        onPlayerStateChange: function (event) {\n            var state = event.data;\n            if (state === this.lastState) {\n                return;\n            }\n            if (event !== null && event.hasOwnProperty('data') && event.data !== null) {\n                switch (event.data) {\n                    case -1:\n                        this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.STARTED, [\n                            this\n                        ]);\n                        if (this.logger !== null) {\n                            this.logger.trace(this.Class.fullName + \" :: PlayerState -1\");\n                        }\n                        break;\n                    case YT.PlayerState.ENDED:\n                        this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.ENDED, [\n                            this\n                        ]);\n                        clearInterval(this.ytTimeupdateInterval);\n                        if (this.logger !== null) {\n                            this.logger.trace(this.Class.fullName + \" :: PlayerState.ENDED\", state);\n                        }\n                        break;\n                    case YT.PlayerState.PLAYING:\n                        this.ytTimeupdateInterval = setInterval($.proxy(this.onTimeupdate, this), 250);\n                        this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PLAYING, [\n                            this\n                        ]);\n                        this.hideLoader();\n                        if (this.logger !== null) {\n                            this.logger.trace(this.Class.fullName + \" :: PlayerState.PLAYING\", state);\n\n                        }\n                        break;\n                    case YT.PlayerState.PAUSED:\n                        clearInterval(this.ytTimeupdateInterval);\n\n                        this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.PAUSED, [\n                            this\n                        ]);\n                        this.hideLoader();\n                        if (this.logger !== null) {\n                            this.logger.trace(this.Class.fullName + \" :: PlayerState.PAUSED\");\n                        }\n                        break;\n                    case YT.PlayerState.BUFFERING:\n                        this.showLoader();\n                        if (this.logger !== null) {\n                            this.logger.trace(this.Class.fullName + \" :: PlayerState.BUFFERING\");\n                        }\n                        break;\n                    case YT.PlayerState.CUED:\n                        this.hideLoader();\n                        break;\n                }\n            }\n            this.lastState = state;\n        },\n        onPlayerError: function (event) {\n            if (this.logger !== null) {\n                this.logger.warn(this.Class.fullName + \" :: onPlayerError\");\n                this.logger.warn(event);\n            }\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.ERROR, {\n                self: this,\n                errorCode: fr.ina.amalia.player.PlayerErrorCode.MEDIA_FILE_NOT_FOUND\n            });\n            this.ytPlayer.stopVideo();\n            this.ytPlayer.destroy();\n            this.ytPlayer = null;\n        },\n        /**\n         * Fired on full-screen state change\n         * @method onFullscreenHandler\n         * @event fr.ina.amalia.player.PlayerEventType.FULLSCREEN_CHANGE\n         * @param {Object} event\n         */\n        onFullscreenHandler: function (event) {\n            if ($(event.originalEvent.target).attr('id') === event.data.self.mediaContainer.attr('id')) {\n                event.data.self.updateContainerSize();\n                if (event.data.self.logger !== null) {\n                    event.data.self.logger.trace(event.data.self.Class.fullName, \"onFullscreenHandler updateContainerSize\");\n                }\n            }\n        },\n        /**\n         * In charge to play media\n         * @method play\n         */\n        play: function () {\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.playVideo();\n                this._super();\n            }\n        },\n        /**\n         * In charge to pause media\n         * @method pause\n         */\n        pause: function () {\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.pauseVideo();\n                this._super();\n            }\n        },\n        /**\n         * In charge to stop media\n         * @method stop\n         */\n        stop: function () {\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.stopVideo();\n                this._super();\n            }\n        },\n        /**\n         * In charge to set mute state\n         * @method mute\n         * @event fr.ina.amalia.player.PlayerEventType.MUTE\n         * @return {Number}\n         */\n        mute: function () {\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.mute();\n                this._super();\n            }\n        },\n        /**\n         * In charge to set unmute state\n         * @method unmute\n         * @event fr.ina.amalia.player.PlayerEventType.UN_MUTE\n         */\n        unmute: function () {\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.unMute();\n                this._super();\n            }\n        },\n        /**\n         * Return media duration with tc offset\n         * @method getDuration\n         * @return {Number}\n         */\n        getDuration: function () {\n            return this.ytPlayer ? this.ytPlayer.getDuration() : 0;\n        },\n        /**\n         * Returns the current playback volume percentage, as a number from 0 to\n         * 100.\n         * @method getVolume\n         * @return {Number}\n         */\n        getVolume: function () {\n            if (this.ytPlayer !== null) {\n                return this.ytPlayer.getVolume();\n            }\n            return null;\n        },\n        /**\n         * Sets the volume. Accepts an integer between 0 and 100.\n         * @method getVolume\n         * @return {Number}\n         */\n        setVolume: function (volume) {\n            if (this.ytPlayer !== null) {\n                return this.ytPlayer.setVolume(volume);\n            }\n            return null;\n        },\n        /**\n         * Return current position in seconds\n         * @method getCurrentTime\n         * @return {Number}\n         */\n        getCurrentTime: function () {\n            if (this.ytPlayer !== null) {\n                return this.ytPlayer.getCurrentTime() + this.tcOffset;\n            }\n            return this.tcOffset;\n        },\n        /**\n         * Set seek position in seconds\n         * @method setCurrentTime\n         * @param {Object} value\n         * @event fr.ina.amalia.player.PlayerEventType.SEEK\n         * @return return current time without tc offset.\n         */\n        setCurrentTime: function (value) {\n            var currentTime = isNaN(value) ? 0 : value;\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.seekTo(Math.max(0, currentTime - this.tcOffset), true);\n            }\n            this._super(currentTime);\n        },\n        /**\n         * Return true if media is paused\n         * @method isPaused\n         * @return {Boolean}\n         */\n        isPaused: function () {\n            if (this.ytPlayer !== null) {\n                return (this.lastState === YT.PlayerState.PAUSED);\n            }\n            return null;\n        },\n        /**\n         * This function retrieves the playback rate\n         * @returns the current playback speed of the audio/video.\n         */\n        getPlaybackrate: function () {\n            if (this.ytPlayer !== null) {\n                return this.ytPlayer.getPlaybackRate();\n            }\n            return null;\n        },\n        /**\n         * This function sets the suggested playback rate\n         * @param {Objecy} suggested playback rate\n         */\n        setPlaybackrate: function (suggestedRate) {\n            if (this.ytPlayer !== null) {\n                this.ytPlayer.setPlaybackrate(suggestedRate);\n            }\n        },\n        /**\n         * Fired on time change evnet\n         * @method onTimeupdate\n         * @param {Object} event\n         * @event fr.ina.amalia.player.PlayerEventType.TIME_CHANGE\n         */\n        onTimeupdate: function () {\n            var tcOffset = this.getTcOffset();\n            var currentTime = this.getCurrentTime();\n            var duration = this.getDuration() + tcOffset;\n            var percentage = ((currentTime - tcOffset) * 100) / (duration - tcOffset);\n            this.mediaContainer.trigger(fr.ina.amalia.player.PlayerEventType.TIME_CHANGE, {\n                self: this,\n                currentTime: currentTime,\n                duration: duration,\n                percentage: percentage,\n                tcOffset: this.getTcOffset()\n            });\n            // In Range play mode\n            if (this.isRangePlayer === true && typeof this.rangePlayerTcout === \"number\" && this.rangePlayerTcout <= currentTime) {\n                this.pause();\n            }\n            if (typeof this.settings.callbacks.onTimeupdate !== \"undefined\") {\n                try {\n                    /* jslint evil: true */\n                    eval(this.settings.callbacks.onTimeupdate + '(currentTime)');\n                }\n                catch (e) {\n                    if (this.logger !== null) {\n                        this.logger.warn(\"Send callback failed.\");\n                    }\n                }\n            }\n        }\n    });"
  },
  {
    "path": "src/utils/jquery-class.js",
    "content": "(function ($) {\n    // Several of the methods in this plugin use code adapated from Prototype\n    //  Prototype JavaScript framework, version 1.6.0.1\n    //  (c) 2005-2007 Sam Stephenson\n    var regs = {\n            undHash: /_|-/,\n            colons: /::/,\n            words: /([A-Z]+)([A-Z][a-z])/g,\n            lowUp: /([a-z\\d])([A-Z])/g,\n            dash: /([a-z\\d])([A-Z])/g,\n            replacer: /\\{([^\\}]+)\\}/g,\n            dot: /\\./\n        },\n        getNext = function (current, nextPart, add) {\n            return current[nextPart] || (add && (current[nextPart] = {}));\n        },\n        isContainer = function (current) {\n            var type = typeof current;\n            return type && (type == 'function' || type == 'object');\n        },\n        getObject = function (objectName, roots, add) {\n\n            var parts = objectName ? objectName.split(regs.dot) : [],\n                length = parts.length,\n                currents = $.isArray(roots) ? roots : [\n                    roots || window\n                ],\n                current,\n                ret,\n                i,\n                c = 0,\n                type;\n\n            if (length == 0) {\n                return currents[0];\n            }\n            while (current = currents[c++]) {\n                for (i = 0;\n                     i < length - 1 && isContainer(current);\n                     i++) {\n                    current = getNext(current, parts[i], add);\n                }\n                if (isContainer(current)) {\n\n                    ret = getNext(current, parts[i], add);\n\n                    if (ret !== undefined) {\n\n                        if (add === false) {\n                            delete current[parts[i]];\n                        }\n                        return ret;\n\n                    }\n\n                }\n            }\n        },\n        /**\n         * @class jQuery.String\n         *\n         * A collection of useful string helpers.\n         *\n         */\n        str = $.String = $.extend($.String || {}, {\n            /**\n             * @function\n             * Gets an object from a string.\n             * @param {String} name the name of the object to look for\n             * @param {Array} [roots] an array of root objects to look for the name\n             * @param {Boolean} [add] true to add missing objects to\n             *  the path. false to remove found properties. undefined to\n             *  not modify the root object\n             */\n            getObject: getObject,\n            /**\n             * Capitalizes a string\n             * @param {String} s the string.\n             * @return {String} a string with the first character capitalized.\n             */\n            capitalize: function (s, cache) {\n                return s.charAt(0).toUpperCase() + s.substr(1);\n            },\n            /**\n             * Capitalizes a string from something undercored. Examples:\n             * @codestart\n             * jQuery.String.camelize(\"one_two\") //-> \"oneTwo\"\n             * \"three-four\".camelize() //-> threeFour\n             * @codeend\n             * @param {String} s\n             * @return {String} a the camelized string\n             */\n            camelize: function (s) {\n                s = str.classize(s);\n                return s.charAt(0).toLowerCase() + s.substr(1);\n            },\n            /**\n             * Like camelize, but the first part is also capitalized\n             * @param {String} s\n             * @return {String} the classized string\n             */\n            classize: function (s, join) {\n                var parts = s.split(regs.undHash),\n                    i = 0;\n                for (;\n                    i < parts.length;\n                    i++) {\n                    parts[i] = str.capitalize(parts[i]);\n                }\n\n                return parts.join(join || '');\n            },\n            /**\n             * Like [jQuery.String.classize|classize], but a space separates each 'word'\n             * @codestart\n             * jQuery.String.niceName(\"one_two\") //-> \"One Two\"\n             * @codeend\n             * @param {String} s\n             * @return {String} the niceName\n             */\n            niceName: function (s) {\n                str.classize(parts[i], ' ');\n            },\n            /**\n             * Underscores a string.\n             * @codestart\n             * jQuery.String.underscore(\"OneTwo\") //-> \"one_two\"\n             * @codeend\n             * @param {String} s\n             * @return {String} the underscored string\n             */\n            underscore: function (s) {\n                return s.replace(regs.colons, '/').replace(regs.words, '$1_$2').replace(regs.lowUp, '$1_$2').replace(regs.dash, '_').toLowerCase();\n            },\n            /**\n             * Returns a string with {param} replaced values from data.\n             *\n             *     $.String.sub(\"foo {bar}\",{bar: \"far\"})\n             *     //-> \"foo far\"\n             *\n             * @param {String} s The string to replace\n             * @param {Object} data The data to be used to look for properties.  If it's an array, multiple\n             * objects can be used.\n             * @param {Boolean} [remove] if a match is found, remove the property from the object\n             */\n            sub: function (s, data, remove) {\n                var obs = [];\n                obs.push(s.replace(regs.replacer, function (whole, inside) {\n                    //convert inside to type\n                    var ob = getObject(inside, data, typeof remove == 'boolean' ? !remove : remove),\n                        type = typeof ob;\n                    if ((type === 'object' || type === 'function') && type !== null) {\n                        obs.push(ob);\n                        return \"\";\n                    } else {\n                        return \"\" + ob;\n                    }\n                }));\n                return obs.length <= 1 ? obs[0] : obs;\n            }\n        });\n\n})(jQuery);\n(function ($) {\n\n    // if we are initializing a new class\n    var initializing = false,\n        makeArray = $.makeArray,\n        isFunction = $.isFunction,\n        isArray = $.isArray,\n        extend = $.extend,\n        concatArgs = function (arr, args) {\n            return arr.concat(makeArray(args));\n        },\n    // tests if we can get super in .toString()\n        fnTest = /xyz/.test(function () {\n            xyz;\n        }) ? /\\b_super\\b/ : /.*/,\n    // overwrites an object with methods, sets up _super\n    // newProps - new properties\n    // oldProps - where the old properties might be\n    // addTo - what we are adding to\n        inheritProps = function (newProps, oldProps, addTo) {\n            addTo = addTo || newProps\n            for (var name in newProps) {\n                // Check if we're overwriting an existing function\n                addTo[name] = isFunction(newProps[name]) &&\n                isFunction(oldProps[name]) &&\n                fnTest.test(newProps[name]) ? (function (name, fn) {\n                    return function () {\n                        var tmp = this._super,\n                            ret;\n\n                        // Add a new ._super() method that is the same method\n                        // but on the super-class\n                        this._super = oldProps[name];\n\n                        // The method only need to be bound temporarily, so we\n                        // remove it when we're done executing\n                        ret = fn.apply(this, arguments);\n                        this._super = tmp;\n                        return ret;\n                    };\n                })(name, newProps[name]) : newProps[name];\n            }\n        },\n        /**\n         * @class jQuery.Class\n         * @plugin jquery/class\n         * @tag core\n         * @download dist/jquery/jquery.class.js\n         * @test jquery/class/qunit.html\n         *\n         * Class provides simulated inheritance in JavaScript. Use clss to bridge the gap between\n         * jQuery's functional programming style and Object Oriented Programming. It\n         * is based off John Resig's [http://ejohn.org/blog/simple-javascript-inheritance/|Simple Class]\n         * Inheritance library.  Besides prototypal inheritance, it includes a few important features:\n         *\n         *   - Static inheritance\n         *   - Introspection\n         *   - Namespaces\n         *   - Setup and initialization methods\n         *   - Easy callback function creation\n         *\n         *\n         * ## Static v. Prototype\n         *\n         * Before learning about Class, it's important to\n         * understand the difference between\n         * a class's __static__ and __prototype__ properties.\n         *\n         *     //STATIC\n         *     MyClass.staticProperty  //shared property\n         *\n         *     //PROTOTYPE\n         *     myclass = new MyClass()\n         *     myclass.prototypeMethod() //instance method\n         *\n         * A static (or class) property is on the Class constructor\n         * function itself\n         * and can be thought of being shared by all instances of the\n         * Class. Prototype propertes are available only on instances of the Class.\n         *\n         * ## A Basic Class\n         *\n         * The following creates a Monster class with a\n         * name (for introspection), static, and prototype members.\n         * Every time a monster instance is created, the static\n         * count is incremented.\n         *\n         * @codestart\n         * $.Class.extend('Monster',\n         * /* @static *|\n         * {\n         *   count: 0\n         * },\n         * /* @prototype *|\n         * {\n         *   init: function( name ) {\n         *\n         *     // saves name on the monster instance\n         *     this.name = name;\n         *\n         *     // sets the health\n         *     this.health = 10;\n         *\n         *     // increments count\n         *     this.Class.count++;\n         *   },\n         *   eat: function( smallChildren ){\n         *     this.health += smallChildren;\n         *   },\n         *   fight: function() {\n         *     this.health -= 2;\n         *   }\n         * });\n         *\n         * hydra = new Monster('hydra');\n         *\n         * dragon = new Monster('dragon');\n         *\n         * hydra.name        // -> hydra\n         * Monster.count     // -> 2\n         * Monster.shortName // -> 'Monster'\n         *\n         * hydra.eat(2);     // health = 12\n         *\n         * dragon.fight();   // health = 8\n         *\n         * @codeend\n         *\n         *\n         * Notice that the prototype <b>init</b> function is called when a new instance of Monster is created.\n         *\n         *\n         * ## Inheritance\n         *\n         * When a class is extended, all static and prototype properties are available on the new class.\n         * If you overwrite a function, you can call the base class's function by calling\n         * <code>this._super</code>.  Lets create a SeaMonster class.  SeaMonsters are less\n         * efficient at eating small children, but more powerful fighters.\n         *\n         *\n         *     Monster.extend(\"SeaMonster\",{\n         *       eat: function( smallChildren ) {\n         *         this._super(smallChildren / 2);\n         *       },\n         *       fight: function() {\n         *         this.health -= 1;\n         *       }\n         *     });\n         *\n         *     lockNess = new SeaMonster('Lock Ness');\n         *     lockNess.eat(4);   //health = 12\n         *     lockNess.fight();  //health = 11\n         *\n         * ### Static property inheritance\n         *\n         * You can also inherit static properties in the same way:\n         *\n         *     $.Class.extend(\"First\",\n         *     {\n         *         staticMethod: function() { return 1;}\n         *     },{})\n         *\n         *     First.extend(\"Second\",{\n         *         staticMethod: function() { return this._super()+1;}\n         *     },{})\n         *\n         *     Second.staticMethod() // -> 2\n         *\n         * ## Namespaces\n         *\n         * Namespaces are a good idea! We encourage you to namespace all of your code.\n         * It makes it possible to drop your code into another app without problems.\n         * Making a namespaced class is easy:\n         *\n         * @codestart\n         * $.Class.extend(\"MyNamespace.MyClass\",{},{});\n         *\n         * new MyNamespace.MyClass()\n         * @codeend\n         * <h2 id='introspection'>Introspection</h2>\n         * Often, it's nice to create classes whose name helps determine functionality.  Ruby on\n         * Rails's [http://api.rubyonrails.org/classes/ActiveRecord/Base.html|ActiveRecord] ORM class\n         * is a great example of this.  Unfortunately, JavaScript doesn't have a way of determining\n         * an object's name, so the developer must provide a name.  Class fixes this by taking a String name for the class.\n         * @codestart\n         * $.Class.extend(\"MyOrg.MyClass\",{},{})\n         * MyOrg.MyClass.shortName //-> 'MyClass'\n         * MyOrg.MyClass.fullName //->  'MyOrg.MyClass'\n         * @codeend\n         * The fullName (with namespaces) and the shortName (without namespaces) are added to the Class's\n         * static properties.\n         *\n         *\n         * <h2>Setup and initialization methods</h2>\n         * <p>\n         * Class provides static and prototype initialization functions.\n         * These come in two flavors - setup and init.\n         * Setup is called before init and\n         * can be used to 'normalize' init's arguments.\n         * </p>\n         * <div class='whisper'>PRO TIP: Typically, you don't need setup methods in your classes. Use Init instead.\n         * Reserve setup methods for when you need to do complex pre-processing of your class before init is called.\n         *\n         * </div>\n         * @codestart\n         * $.Class.extend(\"MyClass\",\n         * {\n         *   setup: function() {} //static setup\n         *   init: function() {} //static constructor\n         * },\n         * {\n         *   setup: function() {} //prototype setup\n         *   init: function() {} //prototype constructor\n         * })\n         * @codeend\n         *\n         * <h3>Setup</h3>\n         * <p>Setup functions are called before init functions.  Static setup functions are passed\n         * the base class followed by arguments passed to the extend function.\n         * Prototype static functions are passed the Class constructor function arguments.</p>\n         * <p>If a setup function returns an array, that array will be used as the arguments\n         * for the following init method.  This provides setup functions the ability to normalize\n         * arguments passed to the init constructors.  They are also excellent places\n         * to put setup code you want to almost always run.</p>\n         * <p>\n         * The following is similar to how [jQuery.Controller.prototype.setup]\n         * makes sure init is always called with a jQuery element and merged options\n         * even if it is passed a raw\n         * HTMLElement and no second parameter.\n         * </p>\n         * @codestart\n         * $.Class.extend(\"jQuery.Controller\",{\n         *   ...\n         * },{\n         *   setup: function( el, options ) {\n         *     ...\n         *     return [$(el),\n         *             $.extend(true,\n         *                this.Class.defaults,\n         *                options || {} ) ]\n         *   }\n         * })\n         * @codeend\n         * Typically, you won't need to make or overwrite setup functions.\n         * <h3>Init</h3>\n         *\n         * <p>Init functions are called after setup functions.\n         * Typically, they receive the same arguments\n         * as their preceding setup function.  The Foo class's <code>init</code> method\n         * gets called in the following example:\n         * </p>\n         * @codestart\n         * $.Class.Extend(\"Foo\", {\n         *   init: function( arg1, arg2, arg3 ) {\n         *     this.sum = arg1+arg2+arg3;\n         *   }\n         * })\n         * var foo = new Foo(1,2,3);\n         * foo.sum //-> 6\n         * @codeend\n         * <h2>Callbacks</h2>\n         * <p>Similar to jQuery's proxy method, Class provides a\n         * [jQuery.Class.static.callback callback]\n         * function that returns a callback to a method that will always\n         * have\n         * <code>this</code> set to the class or instance of the class.\n         * </p>\n         * The following example uses this.callback to make sure\n         * <code>this.name</code> is available in <code>show</code>.\n         * @codestart\n         * $.Class.extend(\"Todo\",{\n         *   init: function( name ) { this.name = name }\n         *   get: function() {\n         *     $.get(\"/stuff\",this.callback('show'))\n         *   },\n         *   show: function( txt ) {\n         *     alert(this.name+txt)\n         *   }\n         * })\n         * new Todo(\"Trash\").get()\n         * @codeend\n         * <p>Callback is available as a static and prototype method.</p>\n         * <h2>Demo</h2>\n         * @demo jquery/class/class.html\n         *\n         * @constructor Creating a new instance of an object that has extended jQuery.Class\n         *     calls the init prototype function and returns a new instance of the class.\n         *\n         */\n\n        clss = $.Class = function () {\n            if (arguments.length) {\n                clss.extend.apply(clss, arguments);\n            }\n        };\n\n    /* @Static*/\n    extend(clss, {\n        /**\n         * @function callback\n         * Returns a callback function for a function on this Class.\n         * The callback function ensures that 'this' is set appropriately.\n         * @codestart\n         * $.Class.extend(\"MyClass\",{\n         *     getData: function() {\n         *         this.showing = null;\n         *         $.get(\"data.json\",this.callback('gotData'),'json')\n         *     },\n         *     gotData: function( data ) {\n         *         this.showing = data;\n         *     }\n         * },{});\n         * MyClass.showData();\n         * @codeend\n         * <h2>Currying Arguments</h2>\n         * Additional arguments to callback will fill in arguments on the returning function.\n         * @codestart\n         * $.Class.extend(\"MyClass\",{\n         *    getData: function( <b>callback</b> ) {\n         *      $.get(\"data.json\",this.callback('process',<b>callback</b>),'json');\n         *    },\n         *    process: function( <b>callback</b>, jsonData ) { //callback is added as first argument\n         *        jsonData.processed = true;\n         *        callback(jsonData);\n         *    }\n         * },{});\n         * MyClass.getData(showDataFunc)\n         * @codeend\n         * <h2>Nesting Functions</h2>\n         * Callback can take an array of functions to call as the first argument.  When the returned callback function\n         * is called each function in the array is passed the return value of the prior function.  This is often used\n         * to eliminate currying initial arguments.\n         * @codestart\n         * $.Class.extend(\"MyClass\",{\n         *    getData: function( callback ) {\n         *      //calls process, then callback with value from process\n         *      $.get(\"data.json\",this.callback(['process2',callback]),'json') \n         *    },\n         *    process2: function( type,jsonData ) {\n         *        jsonData.processed = true;\n         *        return [jsonData];\n         *    }\n         * },{});\n         * MyClass.getData(showDataFunc);\n         * @codeend\n         * @param {String|Array} fname If a string, it represents the function to be called.\n         * If it is an array, it will call each function in order and pass the return value of the prior function to the\n         * next function.\n         * @return {Function} the callback function.\n         */\n        callback: function (funcs) {\n\n            //args that should be curried\n            var args = makeArray(arguments),\n                self;\n\n            funcs = args.shift();\n\n            if (!isArray(funcs)) {\n                funcs = [\n                    funcs\n                ];\n            }\n\n            self = this;\n\n            return function class_cb() {\n                var cur = concatArgs(args, arguments),\n                    isString,\n                    length = funcs.length,\n                    f = 0,\n                    func;\n\n                for (;\n                    f < length;\n                    f++) {\n                    func = funcs[f];\n                    if (!func) {\n                        continue;\n                    }\n\n                    isString = typeof func == \"string\";\n                    if (isString && self._set_called) {\n                        self.called = func;\n                    }\n                    cur = (isString ? self[func] : func).apply(self, cur || []);\n                    if (f < length - 1) {\n                        cur = !isArray(cur) || cur._use_call ? [\n                            cur\n                        ] : cur\n                    }\n                }\n                return cur;\n            }\n        },\n        /**\n         *   @function getObject\n         *   Gets an object from a String.\n         *   If the object or namespaces the string represent do not\n         *   exist it will create them.\n         *   @codestart\n         *   Foo = {Bar: {Zar: {\"Ted\"}}}\n         *   $.Class.getobject(\"Foo.Bar.Zar\") //-> \"Ted\"\n         *   @codeend\n         *   @param {String} objectName the object you want to get\n         *   @param {Object} [current=window] the object you want to look in.\n         *   @return {Object} the object you are looking for.\n         */\n        getObject: $.String.getObject,\n        /**\n         * @function newInstance\n         * Creates a new instance of the class.  This method is useful for creating new instances\n         * with arbitrary parameters.\n         * <h3>Example</h3>\n         * @codestart\n         * $.Class.extend(\"MyClass\",{},{})\n         * var mc = MyClass.newInstance.apply(null, new Array(parseInt(Math.random()*10,10))\n         * @codeend\n         * @return {class} instance of the class\n         */\n        newInstance: function () {\n            var inst = this.rawInstance(),\n                args;\n            if (inst.setup) {\n                args = inst.setup.apply(inst, arguments);\n            }\n            if (inst.init) {\n                inst.init.apply(inst, isArray(args) ? args : arguments);\n            }\n            return inst;\n        },\n        /**\n         * Setup gets called on the inherting class with the base class followed by the\n         * inheriting class's raw properties.\n         *\n         * Setup will deeply extend a static defaults property on the base class with\n         * properties on the base class.  For example:\n         *\n         *     $.Class(\"MyBase\",{\n         *       defaults : {\n         *         foo: 'bar'\n         *       }\n         *     },{})\n         *\n         *     MyBase(\"Inheriting\",{\n         *       defaults : {\n         *         newProp : 'newVal'\n         *       }\n         *     },{}\n         *\n         *     Inheriting.defaults -> {foo: 'bar', 'newProp': 'newVal'}\n         *\n         * @param {Object} baseClass the base class that is being inherited from\n         * @param {String} fullName the name of the new class\n         * @param {Object} staticProps the static properties of the new class\n         * @param {Object} protoProps the prototype properties of the new class\n         */\n        setup: function (baseClass, fullName) {\n            this.defaults = extend(true, {}, baseClass.defaults, this.defaults);\n            return arguments;\n        },\n        rawInstance: function () {\n            initializing = true;\n            var inst = new this();\n            initializing = false;\n            return inst;\n        },\n        /**\n         * Extends a class with new static and prototype functions.  There are a variety of ways\n         * to use extend:\n         * @codestart\n         * //with className, static and prototype functions\n         * $.Class.extend('Task',{ STATIC },{ PROTOTYPE })\n         * //with just classname and prototype functions\n         * $.Class.extend('Task',{ PROTOTYPE })\n         * //With just a className\n         * $.Class.extend('Task')\n         * @codeend\n         * @param {String} [fullName]  the classes name (used for classes w/ introspection)\n         * @param {Object} [klass]  the new classes static/class functions\n         * @param {Object} [proto]  the new classes prototype functions\n         * @return {jQuery.Class} returns the new class\n         */\n        extend: function (fullName, klass, proto) {\n            // figure out what was passed\n            if (typeof fullName != 'string') {\n                proto = klass;\n                klass = fullName;\n                fullName = null;\n            }\n            if (!proto) {\n                proto = klass;\n                klass = null;\n            }\n\n            proto = proto || {};\n            var _super_class = this,\n                _super = this.prototype,\n                name,\n                shortName,\n                namespace,\n                prototype;\n\n            // Instantiate a base class (but only create the instance,\n            // don't run the init constructor)\n            initializing = true;\n            prototype = new this();\n            initializing = false;\n            // Copy the properties over onto the new prototype\n            inheritProps(proto, _super, prototype);\n\n            // The dummy class constructor\n\n            function Class() {\n                // All construction is actually done in the init method\n                if (initializing)\n                    return;\n\n                if (this.constructor !== Class && arguments.length) { //we are being called w/o new\n                    return arguments.callee.extend.apply(arguments.callee, arguments)\n                } else { //we are being called w/ new\n                    return this.Class.newInstance.apply(this.Class, arguments)\n                }\n            }\n\n            // Copy old stuff onto class\n            for (name in this) {\n                if (this.hasOwnProperty(name)) {\n                    Class[name] = this[name];\n                }\n            }\n\n            // copy new props on class\n            inheritProps(klass, this, Class);\n\n            // do namespace stuff\n            if (fullName) {\n\n                var parts = fullName.split(/\\./),\n                    shortName = parts.pop(),\n                    current = clss.getObject(parts.join('.'), window, true),\n                    namespace = current;\n\n\n                current[shortName] = Class;\n            }\n\n            // set things that can't be overwritten\n            extend(Class, {\n                prototype: prototype,\n                namespace: namespace,\n                shortName: shortName,\n                constructor: Class,\n                fullName: fullName\n            });\n\n            //make sure our prototype looks nice\n            Class.prototype.Class = Class.prototype.constructor = Class;\n\n\n            /**\n             * @attribute fullName\n             * The full name of the class, including namespace, provided for introspection purposes.\n             * @codestart\n             * $.Class.extend(\"MyOrg.MyClass\",{},{})\n             * MyOrg.MyClass.shortName //-> 'MyClass'\n             * MyOrg.MyClass.fullName //->  'MyOrg.MyClass'\n             * @codeend\n             */\n\n            var args = Class.setup.apply(Class, concatArgs([\n                _super_class\n            ], arguments));\n\n            if (Class.init) {\n                Class.init.apply(Class, args || []);\n            }\n\n            /* @Prototype*/\n            return Class;\n            /**\n             * @function setup\n             * If a setup method is provided, it is called when a new\n             * instances is created.  It gets passed the same arguments that\n             * were given to the Class constructor function (<code> new Class( arguments ... )</code>).\n             *\n             *     $.Class(\"MyClass\",\n             *     {\n             *        setup: function( val ) {\n             *           this.val = val;\n             *         }\n             *     })\n             *     var mc = new MyClass(\"Check Check\")\n             *     mc.val //-> 'Check Check'\n             *\n             * Setup is called before [jQuery.Class.prototype.init init].  If setup\n             * return an array, those arguments will be used for init.\n             *\n             *     $.Class(\"jQuery.Controller\",{\n             *       setup : function(htmlElement, rawOptions){\n             *         return [$(htmlElement), \n             *                   $.extend({}, this.Class.defaults, rawOptions )] \n             *       }\n             *     })\n             *\n             * <div class='whisper'>PRO TIP:\n             * Setup functions are used to normalize constructor arguments and provide a place for\n             * setup code that extending classes don't have to remember to call _super to\n             * run.\n             * </div>\n             *\n             * Setup is not defined on $.Class itself, so calling super in inherting classes\n             * will break.  Don't do the following:\n             *\n             *     $.Class(\"Thing\",{\n             *       setup : function(){\n             *         this._super(); // breaks!\n             *       }\n             *     })\n             *\n             * @return {Array|undefined} If an array is return, [jQuery.Class.prototype.init] is\n             * called with those arguments; otherwise, the original arguments are used.\n             */\n            //break up\n            /**\n             * @function init\n             * If an <code>init</code> method is provided, it gets called when a new instance\n             * is created.  Init gets called after [jQuery.Class.prototype.setup setup], typically with the\n             * same arguments passed to the Class\n             * constructor: (<code> new Class( arguments ... )</code>).\n             *\n             *     $.Class(\"MyClass\",\n             *     {\n             *        init: function( val ) {\n             *           this.val = val;\n             *        }\n             *     })\n             *     var mc = new MyClass(1)\n             *     mc.val //-> 1\n             *\n             * [jQuery.Class.prototype.setup Setup] is able to modify the arguments passed to init.  Read\n             * about it there.\n             *\n             */\n            //Breaks up code\n            /**\n             * @attribute Class\n             * References the static properties of the instance's class.\n             * <h3>Quick Example</h3>\n             * @codestart\n             * // a class with a static classProperty property\n             * $.Class.extend(\"MyClass\", {classProperty : true}, {});\n             *\n             * // a new instance of myClass\n             * var mc1 = new MyClass();\n             *\n             * //\n             * mc1.Class.classProperty = false;\n             *\n             * // creates a new MyClass\n             * var mc2 = new mc.Class();\n             * @codeend\n             * Getting static properties via the Class property, such as it's\n             * [jQuery.Class.static.fullName fullName] is very common.\n             */\n        }\n\n    })\n\n\n    clss.prototype.\n    /**\n     * @function callback\n     * Returns a callback function.  This does the same thing as and is described better in [jQuery.Class.static.callback].\n     * The only difference is this callback works\n     * on a instance instead of a class.\n     * @param {String|Array} fname If a string, it represents the function to be called.\n     * If it is an array, it will call each function in order and pass the return value of the prior function to the\n     * next function.\n     * @return {Function} the callback function\n     */\n        callback = clss.callback;\n\n\n})(jQuery)"
  },
  {
    "path": "src/utils/jquery-debouncedresize.js",
    "content": "/*\n * debouncedresize: special jQuery event that happens once after a window resize\n * \n * latest version and complete README available on Github:\n * https://github.com/louisremi/jquery-smartresize\n * \n * Copyright 2012 @louis_remi Licensed under the MIT license.\n * \n * This saved you an hour of work? Send me music\n * http://www.amazon.co.uk/wishlist/HNTU0468LQON\n */\n(function ($) {\n\n    var $event = $.event,\n        $special,\n        resizeTimeout;\n\n    $special = $event.special.debouncedresize = {\n        setup: function () {\n            $(this).on(\"resize\", $special.handler);\n        },\n        teardown: function () {\n            $(this).off(\"resize\", $special.handler);\n        },\n        handler: function (event, execAsap) {\n            // Save the context\n            var context = this,\n                args = arguments,\n                dispatch = function () {\n                    // set correct event type\n                    event.type = \"debouncedresize\";\n                    $event.dispatch.apply(context, args);\n                };\n\n            if (resizeTimeout) {\n                clearTimeout(resizeTimeout);\n            }\n\n            if (execAsap) {\n                dispatch();\n            }\n            {\n                resizeTimeout = setTimeout(dispatch, $special.threshold);\n            }\n        },\n        threshold: 150\n    };\n\n})(jQuery);\n"
  },
  {
    "path": "src/utils/jquery-knob.js",
    "content": "/*!jQuery Knob*/\n/**\n * Downward compatible, touchable dial\n *\n * Version: 1.2.12\n * Requires: jQuery v1.7+\n *\n * Copyright (c) 2012 Anthony Terrien\n * Under MIT License (http://www.opensource.org/licenses/mit-license.php)\n *\n * Thanks to vor, eskimoblood, spiffistan, FabrizioC\n */\n(function (factory) {\n    if (typeof exports === 'object') {\n        // CommonJS\n        module.exports = factory(require('jquery'));\n    } else if (typeof define === 'function' && define.amd) {\n        // AMD. Register as an anonymous module.\n        define(['jquery'], factory);\n    } else {\n        // Browser globals\n        factory(jQuery);\n    }\n}(function ($) {\n\n    /**\n     * Kontrol library\n     */\n    \"use strict\";\n\n    /**\n     * Definition of globals and core\n     */\n    var k = {}, // kontrol\n        max = Math.max,\n        min = Math.min;\n\n    k.c = {};\n    k.c.d = $(document);\n    k.c.t = function (e) {\n        return e.originalEvent.touches.length - 1;\n    };\n\n    /**\n     * Kontrol Object\n     *\n     * Definition of an abstract UI control\n     *\n     * Each concrete component must call this one.\n     * <code>\n     * k.o.call(this);\n     * </code>\n     */\n    k.o = function () {\n        var s = this;\n\n        this.o = null; // array of options\n        this.$ = null; // jQuery wrapped element\n        this.i = null; // mixed HTMLInputElement or array of HTMLInputElement\n        this.g = null; // deprecated 2D graphics context for 'pre-rendering'\n        this.v = null; // value ; mixed array or integer\n        this.cv = null; // change value ; not commited value\n        this.x = 0; // canvas x position\n        this.y = 0; // canvas y position\n        this.w = 0; // canvas width\n        this.h = 0; // canvas height\n        this.$c = null; // jQuery canvas element\n        this.c = null; // rendered canvas context\n        this.t = 0; // touches index\n        this.isInit = false;\n        this.fgColor = null; // main color\n        this.pColor = null; // previous color\n        this.dH = null; // draw hook\n        this.cH = null; // change hook\n        this.eH = null; // cancel hook\n        this.rH = null; // release hook\n        this.scale = 1; // scale factor\n        this.relative = false;\n        this.relativeWidth = false;\n        this.relativeHeight = false;\n        this.$div = null; // component div\n\n        this.run = function () {\n            var cf = function (e, conf) {\n                var k;\n                for (k in conf) {\n                    s.o[k] = conf[k];\n                }\n                s._carve().init();\n                s._configure()\n                    ._draw();\n            };\n\n            if (this.$.data('kontroled')) return;\n            this.$.data('kontroled', true);\n\n            this.extend();\n            this.o = $.extend({\n                    // Config\n                    min: this.$.data('min') !== undefined ? this.$.data('min') : 0,\n                    max: this.$.data('max') !== undefined ? this.$.data('max') : 100,\n                    stopper: true,\n                    readOnly: this.$.data('readonly') || (this.$.attr('readonly') === 'readonly'),\n\n                    // UI\n                    cursor: this.$.data('cursor') === true && 30\n                    || this.$.data('cursor') || 0,\n                    thickness: this.$.data('thickness')\n                    && Math.max(Math.min(this.$.data('thickness'), 1), 0.01)\n                    || 0.35,\n                    lineCap: this.$.data('linecap') || 'butt',\n                    width: this.$.data('width') || 200,\n                    height: this.$.data('height') || 200,\n                    displayInput: this.$.data('displayinput') == null || this.$.data('displayinput'),\n                    displayPrevious: this.$.data('displayprevious'),\n                    fgColor: this.$.data('fgcolor') || '#87CEEB',\n                    inputColor: this.$.data('inputcolor'),\n                    font: this.$.data('font') || 'Arial',\n                    fontWeight: this.$.data('font-weight') || 'bold',\n                    inline: false,\n                    step: this.$.data('step') || 1,\n                    rotation: this.$.data('rotation'),\n\n                    // Hooks\n                    draw: null, // function () {}\n                    change: null, // function (value) {}\n                    cancel: null, // function () {}\n                    release: null, // function (value) {}\n\n                    // Output formatting, allows to add unit: %, ms ...\n                    format: function (v) {\n                        return v;\n                    },\n                    parse: function (v) {\n                        return parseFloat(v);\n                    }\n                }, this.o\n            );\n\n            // finalize options\n            this.o.flip = this.o.rotation === 'anticlockwise' || this.o.rotation === 'acw';\n            if (!this.o.inputColor) {\n                this.o.inputColor = this.o.fgColor;\n            }\n\n            // routing value\n            if (this.$.is('fieldset')) {\n\n                // fieldset = array of integer\n                this.v = {};\n                this.i = this.$.find('input');\n                this.i.each(function (k) {\n                    var $this = $(this);\n                    s.i[k] = $this;\n                    s.v[k] = s.o.parse($this.val());\n\n                    $this.bind(\n                        'change blur',\n                        function () {\n                            var val = {};\n                            val[k] = $this.val();\n                            s.val(s._validate(val));\n                        }\n                    );\n                });\n                this.$.find('legend').remove();\n            } else {\n\n                // input = integer\n                this.i = this.$;\n                this.v = this.o.parse(this.$.val());\n                this.v === '' && (this.v = this.o.min);\n                this.$.bind(\n                    'change blur',\n                    function () {\n                        s.val(s._validate(s.o.parse(s.$.val())));\n                    }\n                );\n\n            }\n\n            !this.o.displayInput && this.$.hide();\n\n            // adds needed DOM elements (canvas, div)\n            this.$c = $(document.createElement('canvas')).attr({\n                width: this.o.width,\n                height: this.o.height\n            });\n\n            // wraps all elements in a div\n            // add to DOM before Canvas init is triggered\n            this.$div = $('<div style=\"'\n                + (this.o.inline ? 'display:inline;' : '')\n                + 'width:' + this.o.width + 'px;height:' + this.o.height + 'px;'\n                + '\"></div>');\n\n            this.$.wrap(this.$div).before(this.$c);\n            this.$div = this.$.parent();\n\n            if (typeof G_vmlCanvasManager !== 'undefined') {\n                G_vmlCanvasManager.initElement(this.$c[0]);\n            }\n\n            this.c = this.$c[0].getContext ? this.$c[0].getContext('2d') : null;\n\n            if (!this.c) {\n                throw {\n                    name: \"CanvasNotSupportedException\",\n                    message: \"Canvas not supported. Please use excanvas on IE8.0.\",\n                    toString: function () {\n                        return this.name + \": \" + this.message\n                    }\n                }\n            }\n\n            // hdpi support\n            this.scale = (window.devicePixelRatio || 1) / (\n                    this.c.webkitBackingStorePixelRatio ||\n                    this.c.mozBackingStorePixelRatio ||\n                    this.c.msBackingStorePixelRatio ||\n                    this.c.oBackingStorePixelRatio ||\n                    this.c.backingStorePixelRatio || 1\n                );\n\n            // detects relative width / height\n            this.relativeWidth = this.o.width % 1 !== 0\n                && this.o.width.indexOf('%');\n            this.relativeHeight = this.o.height % 1 !== 0\n                && this.o.height.indexOf('%');\n            this.relative = this.relativeWidth || this.relativeHeight;\n\n            // computes size and carves the component\n            this._carve();\n\n            // prepares props for transaction\n            if (this.v instanceof Object) {\n                this.cv = {};\n                this.copy(this.v, this.cv);\n            } else {\n                this.cv = this.v;\n            }\n\n            // binds configure event\n            this.$\n                .bind(\"configure\", cf)\n                .parent()\n                .bind(\"configure\", cf);\n\n            // finalize init\n            this._listen()\n                ._configure()\n                ._xy()\n                .init();\n\n            this.isInit = true;\n\n            this.$.val(this.o.format(this.v));\n            this._draw();\n\n            return this;\n        };\n\n        this._carve = function () {\n            if (this.relative) {\n                var w = this.relativeWidth ?\n                    this.$div.parent().width() *\n                    parseInt(this.o.width) / 100\n                        : this.$div.parent().width(),\n                    h = this.relativeHeight ?\n                    this.$div.parent().height() *\n                    parseInt(this.o.height) / 100\n                        : this.$div.parent().height();\n\n                // apply relative\n                this.w = this.h = Math.min(w, h);\n            } else {\n                this.w = this.o.width;\n                this.h = this.o.height;\n            }\n\n            // finalize div\n            this.$div.css({\n                'width': this.w + 'px',\n                'height': this.h + 'px'\n            });\n\n            // finalize canvas with computed width\n            this.$c.attr({\n                width: this.w,\n                height: this.h\n            });\n\n            // scaling\n            if (this.scale !== 1) {\n                this.$c[0].width = this.$c[0].width * this.scale;\n                this.$c[0].height = this.$c[0].height * this.scale;\n                this.$c.width(this.w);\n                this.$c.height(this.h);\n            }\n\n            return this;\n        };\n\n        this._draw = function () {\n\n            // canvas pre-rendering\n            var d = true;\n\n            s.g = s.c;\n\n            s.clear();\n\n            s.dH && (d = s.dH());\n\n            d !== false && s.draw();\n        };\n\n        this._touch = function (e) {\n            var touchMove = function (e) {\n                var v = s.xy2val(\n                    e.originalEvent.touches[s.t].pageX,\n                    e.originalEvent.touches[s.t].pageY\n                );\n\n                if (v == s.cv) return;\n\n                if (s.cH && s.cH(v) === false) return;\n\n                s.change(s._validate(v));\n                s._draw();\n            };\n\n            // get touches index\n            this.t = k.c.t(e);\n\n            // First touch\n            touchMove(e);\n\n            // Touch events listeners\n            k.c.d\n                .bind(\"touchmove.k\", touchMove)\n                .bind(\n                    \"touchend.k\",\n                    function () {\n                        k.c.d.unbind('touchmove.k touchend.k');\n                        s.val(s.cv);\n                    }\n                );\n\n            return this;\n        };\n\n        this._mouse = function (e) {\n            var mouseMove = function (e) {\n                var v = s.xy2val(e.pageX, e.pageY);\n\n                if (v == s.cv) return;\n\n                if (s.cH && (s.cH(v) === false)) return;\n\n                s.change(s._validate(v));\n                s._draw();\n            };\n\n            // First click\n            mouseMove(e);\n\n            // Mouse events listeners\n            k.c.d\n                .bind(\"mousemove.k\", mouseMove)\n                .bind(\n                    // Escape key cancel current change\n                    \"keyup.k\",\n                    function (e) {\n                        if (e.keyCode === 27) {\n                            k.c.d.unbind(\"mouseup.k mousemove.k keyup.k\");\n\n                            if (s.eH && s.eH() === false)\n                                return;\n\n                            s.cancel();\n                        }\n                    }\n                )\n                .bind(\n                    \"mouseup.k\",\n                    function (e) {\n                        k.c.d.unbind('mousemove.k mouseup.k keyup.k');\n                        s.val(s.cv);\n                    }\n                );\n\n            return this;\n        };\n\n        this._xy = function () {\n            var o = this.$c.offset();\n            this.x = o.left;\n            this.y = o.top;\n\n            return this;\n        };\n\n        this._listen = function () {\n            if (!this.o.readOnly) {\n                this.$c\n                    .bind(\n                        \"mousedown\",\n                        function (e) {\n                            e.preventDefault();\n                            s._xy()._mouse(e);\n                        }\n                    )\n                    .bind(\n                        \"touchstart\",\n                        function (e) {\n                            e.preventDefault();\n                            s._xy()._touch(e);\n                        }\n                    );\n\n                this.listen();\n            } else {\n                this.$.attr('readonly', 'readonly');\n            }\n\n            if (this.relative) {\n                $(window).resize(function () {\n                    s._carve().init();\n                    s._draw();\n                });\n            }\n\n            return this;\n        };\n\n        this._configure = function () {\n\n            // Hooks\n            if (this.o.draw) this.dH = this.o.draw;\n            if (this.o.change) this.cH = this.o.change;\n            if (this.o.cancel) this.eH = this.o.cancel;\n            if (this.o.release) this.rH = this.o.release;\n\n            if (this.o.displayPrevious) {\n                this.pColor = this.h2rgba(this.o.fgColor, \"0.4\");\n                this.fgColor = this.h2rgba(this.o.fgColor, \"0.6\");\n            } else {\n                this.fgColor = this.o.fgColor;\n            }\n\n            return this;\n        };\n\n        this._clear = function () {\n            this.$c[0].width = this.$c[0].width;\n        };\n\n        this._validate = function (v) {\n            var val = (~~(((v < 0) ? -0.5 : 0.5) + (v / this.o.step))) * this.o.step;\n            return Math.round(val * 100) / 100;\n        };\n\n        // Abstract methods\n        this.listen = function () {\n        }; // on start, one time\n        this.extend = function () {\n        }; // each time configure triggered\n        this.init = function () {\n        }; // each time configure triggered\n        this.change = function (v) {\n        }; // on change\n        this.val = function (v) {\n        }; // on release\n        this.xy2val = function (x, y) {\n        }; //\n        this.draw = function () {\n        }; // on change / on release\n        this.clear = function () {\n            this._clear();\n        };\n\n        // Utils\n        this.h2rgba = function (h, a) {\n            var rgb;\n            h = h.substring(1, 7);\n            rgb = [\n                parseInt(h.substring(0, 2), 16),\n                parseInt(h.substring(2, 4), 16),\n                parseInt(h.substring(4, 6), 16)\n            ];\n\n            return \"rgba(\" + rgb[0] + \",\" + rgb[1] + \",\" + rgb[2] + \",\" + a + \")\";\n        };\n\n        this.copy = function (f, t) {\n            for (var i in f) {\n                t[i] = f[i];\n            }\n        };\n    };\n\n\n    /**\n     * k.Dial\n     */\n    k.Dial = function () {\n        k.o.call(this);\n\n        this.startAngle = null;\n        this.xy = null;\n        this.radius = null;\n        this.lineWidth = null;\n        this.cursorExt = null;\n        this.w2 = null;\n        this.PI2 = 2 * Math.PI;\n\n        this.extend = function () {\n            this.o = $.extend({\n                bgColor: this.$.data('bgcolor') || '#EEEEEE',\n                angleOffset: this.$.data('angleoffset') || 0,\n                angleArc: this.$.data('anglearc') || 360,\n                inline: true\n            }, this.o);\n        };\n\n        this.val = function (v, triggerRelease) {\n            if (null != v) {\n\n                // reverse format\n                v = this.o.parse(v);\n\n                if (triggerRelease !== false\n                    && v != this.v\n                    && this.rH\n                    && this.rH(v) === false) {\n                    return;\n                }\n\n                this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;\n                this.v = this.cv;\n                this.$.val(this.o.format(this.v));\n                this._draw();\n            } else {\n                return this.v;\n            }\n        };\n\n        this.xy2val = function (x, y) {\n            var a, ret;\n\n            a = Math.atan2(\n                    x - (this.x + this.w2),\n                    -(y - this.y - this.w2)\n                ) - this.angleOffset;\n\n            if (this.o.flip) {\n                a = this.angleArc - a - this.PI2;\n            }\n\n            if (this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {\n\n                // if isset angleArc option, set to min if .5 under min\n                a = 0;\n            } else if (a < 0) {\n                a += this.PI2;\n            }\n\n            ret = (a * (this.o.max - this.o.min) / this.angleArc) + this.o.min;\n\n            this.o.stopper && (ret = max(min(ret, this.o.max), this.o.min));\n\n            return ret;\n        };\n\n        this.listen = function () {\n\n            // bind MouseWheel\n            var s = this, mwTimerStop,\n                mwTimerRelease,\n                mw = function (e) {\n                    e.preventDefault();\n\n                    var ori = e.originalEvent,\n                        deltaX = ori.detail || ori.wheelDeltaX,\n                        deltaY = ori.detail || ori.wheelDeltaY,\n                        v = s._validate(s.o.parse(s.$.val()))\n                            + (\n                                deltaX > 0 || deltaY > 0\n                                    ? s.o.step\n                                    : deltaX < 0 || deltaY < 0 ? -s.o.step : 0\n                            );\n\n                    v = max(min(v, s.o.max), s.o.min);\n\n                    s.val(v, false);\n\n                    if (s.rH) {\n                        // Handle mousewheel stop\n                        clearTimeout(mwTimerStop);\n                        mwTimerStop = setTimeout(function () {\n                            s.rH(v);\n                            mwTimerStop = null;\n                        }, 100);\n\n                        // Handle mousewheel releases\n                        if (!mwTimerRelease) {\n                            mwTimerRelease = setTimeout(function () {\n                                if (mwTimerStop)\n                                    s.rH(v);\n                                mwTimerRelease = null;\n                            }, 200);\n                        }\n                    }\n                },\n                kval,\n                to,\n                m = 1,\n                kv = {\n                    37: -s.o.step,\n                    38: s.o.step,\n                    39: s.o.step,\n                    40: -s.o.step\n                };\n\n            this.$\n                .bind(\n                    \"keydown\",\n                    function (e) {\n                        var kc = e.keyCode;\n\n                        // numpad support\n                        if (kc >= 96 && kc <= 105) {\n                            kc = e.keyCode = kc - 48;\n                        }\n\n                        kval = parseInt(String.fromCharCode(kc));\n\n                        if (isNaN(kval)) {\n                            (kc !== 13)                     // enter\n                            && kc !== 8                     // bs\n                            && kc !== 9                     // tab\n                            && kc !== 189                   // -\n                            && (kc !== 190\n                            || s.$.val().match(/\\./))   // . allowed once\n                            && e.preventDefault();\n\n                            // arrows\n                            if ($.inArray(kc, [37, 38, 39, 40]) > -1) {\n                                e.preventDefault();\n\n                                var v = s.o.parse(s.$.val()) + kv[kc] * m;\n                                s.o.stopper && (v = max(min(v, s.o.max), s.o.min));\n\n                                s.change(s._validate(v));\n                                s._draw();\n\n                                // long time keydown speed-up\n                                to = window.setTimeout(function () {\n                                    m *= 2;\n                                }, 30);\n                            }\n                        }\n                    }\n                )\n                .bind(\n                    \"keyup\",\n                    function (e) {\n                        if (isNaN(kval)) {\n                            if (to) {\n                                window.clearTimeout(to);\n                                to = null;\n                                m = 1;\n                                s.val(s.$.val());\n                            }\n                        } else {\n                            // kval postcond\n                            (s.$.val() > s.o.max && s.$.val(s.o.max))\n                            || (s.$.val() < s.o.min && s.$.val(s.o.min));\n                        }\n                    }\n                );\n\n            this.$c.bind(\"mousewheel DOMMouseScroll\", mw);\n            this.$.bind(\"mousewheel DOMMouseScroll\", mw);\n        };\n\n        this.init = function () {\n            if (this.v < this.o.min\n                || this.v > this.o.max) {\n                this.v = this.o.min;\n            }\n\n            this.$.val(this.v);\n            this.w2 = this.w / 2;\n            this.cursorExt = this.o.cursor / 100;\n            this.xy = this.w2 * this.scale;\n            this.lineWidth = this.xy * this.o.thickness;\n            this.lineCap = this.o.lineCap;\n            this.radius = this.xy - this.lineWidth / 2;\n\n            this.o.angleOffset\n            && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);\n\n            this.o.angleArc\n            && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);\n\n            // deg to rad\n            this.angleOffset = this.o.angleOffset * Math.PI / 180;\n            this.angleArc = this.o.angleArc * Math.PI / 180;\n\n            // compute start and end angles\n            this.startAngle = 1.5 * Math.PI + this.angleOffset;\n            this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;\n\n            var s = max(\n                    String(Math.abs(this.o.max)).length,\n                    String(Math.abs(this.o.min)).length,\n                    2\n                ) + 2;\n\n            this.o.displayInput\n            && this.i.css({\n                'width': ((this.w / 2 + 4) >> 0) + 'px',\n                'height': ((this.w / 3) >> 0) + 'px',\n                'position': 'absolute',\n                'vertical-align': 'middle',\n                'margin-top': ((this.w / 3) >> 0) + 'px',\n                'margin-left': '-' + ((this.w * 3 / 4 + 2) >> 0) + 'px',\n                'border': 0,\n                'background': 'none',\n                'font': this.o.fontWeight + ' ' + ((this.w / s) >> 0) + 'px ' + this.o.font,\n                'text-align': 'center',\n                'color': this.o.inputColor || this.o.fgColor,\n                'padding': '0px',\n                '-webkit-appearance': 'none'\n            }) || this.i.css({\n                'width': '0px',\n                'visibility': 'hidden'\n            });\n        };\n\n        this.change = function (v) {\n            this.cv = v;\n            this.$.val(this.o.format(v));\n        };\n\n        this.angle = function (v) {\n            return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);\n        };\n\n        this.arc = function (v) {\n            var sa, ea;\n            v = this.angle(v);\n            if (this.o.flip) {\n                sa = this.endAngle + 0.00001;\n                ea = sa - v - 0.00001;\n            } else {\n                sa = this.startAngle - 0.00001;\n                ea = sa + v + 0.00001;\n            }\n            this.o.cursor\n            && (sa = ea - this.cursorExt)\n            && (ea = ea + this.cursorExt);\n\n            return {\n                s: sa,\n                e: ea,\n                d: this.o.flip && !this.o.cursor\n            };\n        };\n\n        this.draw = function () {\n            var c = this.g,                 // context\n                a = this.arc(this.cv),      // Arc\n                pa,                         // Previous arc\n                r = 1;\n\n            c.lineWidth = this.lineWidth;\n            c.lineCap = this.lineCap;\n\n            if (this.o.bgColor !== \"none\") {\n                c.beginPath();\n                c.strokeStyle = this.o.bgColor;\n                c.arc(this.xy, this.xy, this.radius, this.endAngle - 0.00001, this.startAngle + 0.00001, true);\n                c.stroke();\n            }\n\n            if (this.o.displayPrevious) {\n                pa = this.arc(this.v);\n                c.beginPath();\n                c.strokeStyle = this.pColor;\n                c.arc(this.xy, this.xy, this.radius, pa.s, pa.e, pa.d);\n                c.stroke();\n                r = this.cv == this.v;\n            }\n\n            c.beginPath();\n            c.strokeStyle = r ? this.o.fgColor : this.fgColor;\n            c.arc(this.xy, this.xy, this.radius, a.s, a.e, a.d);\n            c.stroke();\n        };\n\n        this.cancel = function () {\n            this.val(this.v);\n        };\n    };\n\n    $.fn.dial = $.fn.knob = function (o) {\n        return this.each(\n            function () {\n                var d = new k.Dial();\n                d.o = o;\n                d.$ = $(this);\n                d.run();\n            }\n        ).parent();\n    };\n\n}));"
  },
  {
    "path": "src/utils/jquery.hotkeys.js",
    "content": "/*jslint browser: true*/\n/*jslint jquery: true*/\n\n/*\n * jQuery Hotkeys Plugin\n * Copyright 2010, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n *\n * Based upon the plugin by Tzury Bar Yochay:\n * https://github.com/tzuryby/jquery.hotkeys\n *\n * Original idea by:\n * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/\n */\n\n/*\n * One small change is: now keys are passed by object { keys: '...' }\n * Might be useful, when you want to pass some other data to your handler\n */\n(function (jQuery) {\n\n    jQuery.hotkeys = {\n        version: \"0.8\",\n        specialKeys: {\n            8: \"backspace\",\n            9: \"tab\",\n            10: \"return\",\n            13: \"return\",\n            16: \"shift\",\n            17: \"ctrl\",\n            18: \"alt\",\n            19: \"pause\",\n            20: \"capslock\",\n            27: \"esc\",\n            32: \"space\",\n            33: \"pageup\",\n            34: \"pagedown\",\n            35: \"end\",\n            36: \"home\",\n            37: \"left\",\n            38: \"up\",\n            39: \"right\",\n            40: \"down\",\n            45: \"insert\",\n            46: \"del\",\n            59: \";\",\n            61: \"=\",\n            96: \"0\",\n            97: \"1\",\n            98: \"2\",\n            99: \"3\",\n            100: \"4\",\n            101: \"5\",\n            102: \"6\",\n            103: \"7\",\n            104: \"8\",\n            105: \"9\",\n            106: \"*\",\n            107: \"+\",\n            109: \"-\",\n            110: \".\",\n            111: \"/\",\n            112: \"f1\",\n            113: \"f2\",\n            114: \"f3\",\n            115: \"f4\",\n            116: \"f5\",\n            117: \"f6\",\n            118: \"f7\",\n            119: \"f8\",\n            120: \"f9\",\n            121: \"f10\",\n            122: \"f11\",\n            123: \"f12\",\n            144: \"numlock\",\n            145: \"scroll\",\n            173: \"-\",\n            186: \";\",\n            187: \"=\",\n            188: \",\",\n            189: \"-\",\n            190: \".\",\n            191: \"/\",\n            192: \"`\",\n            219: \"[\",\n            220: \"\\\\\",\n            221: \"]\",\n            222: \"'\"\n        },\n        shiftNums: {\n            \"`\": \"~\",\n            \"1\": \"!\",\n            \"2\": \"@\",\n            \"3\": \"#\",\n            \"4\": \"$\",\n            \"5\": \"%\",\n            \"6\": \"^\",\n            \"7\": \"&\",\n            \"8\": \"*\",\n            \"9\": \"(\",\n            \"0\": \")\",\n            \"-\": \"_\",\n            \"=\": \"+\",\n            \";\": \": \",\n            \"'\": \"\\\"\",\n            \",\": \"<\",\n            \".\": \">\",\n            \"/\": \"?\",\n            \"\\\\\": \"|\"\n        },\n        // excludes: button, checkbox, file, hidden, image, password, radio, reset, search, submit, url\n        textAcceptingInputTypes: [\n            \"text\",\n            \"password\",\n            \"number\",\n            \"email\",\n            \"url\",\n            \"range\",\n            \"date\",\n            \"month\",\n            \"week\",\n            \"time\",\n            \"datetime\",\n            \"datetime-local\",\n            \"search\",\n            \"color\",\n            \"tel\"\n        ],\n        // default input types not to bind to unless bound directly\n        textInputTypes: /textarea|input|select/i,\n        options: {\n            filterInputAcceptingElements: true,\n            filterTextInputs: true,\n            filterContentEditable: true\n        }\n    };\n\n    function keyHandler(handleObj) {\n        if (typeof handleObj.data === \"string\") {\n            handleObj.data = {\n                keys: handleObj.data\n            };\n        }\n\n        // Only care when a possible input has been specified\n        if (!handleObj.data || !handleObj.data.keys || typeof handleObj.data.keys !== \"string\") {\n            return;\n        }\n\n        var origHandler = handleObj.handler,\n            keys = handleObj.data.keys.toLowerCase().split(\" \");\n\n        handleObj.handler = function (event) {\n            //      Don't fire in text-accepting inputs that we didn't directly bind to\n            if (this !== event.target &&\n                (jQuery.hotkeys.options.filterInputAcceptingElements &&\n                jQuery.hotkeys.textInputTypes.test(event.target.nodeName) ||\n                (jQuery.hotkeys.options.filterContentEditable && jQuery(event.target).attr('contenteditable')) ||\n                (jQuery.hotkeys.options.filterTextInputs &&\n                jQuery.inArray(event.target.type, jQuery.hotkeys.textAcceptingInputTypes) > -1))) {\n                return;\n            }\n\n            var special = event.type !== \"keypress\" && jQuery.hotkeys.specialKeys[event.which],\n                character = String.fromCharCode(event.which).toLowerCase(),\n                modif = \"\",\n                possible = {};\n\n            jQuery.each([\n                \"alt\",\n                \"ctrl\",\n                \"shift\"\n            ], function (index, specialKey) {\n\n                if (event[specialKey + 'Key'] && special !== specialKey) {\n                    modif += specialKey + '+';\n                }\n            });\n\n            // metaKey is triggered off ctrlKey erronously\n            if (event.metaKey && !event.ctrlKey && special !== \"meta\") {\n                modif += \"meta+\";\n            }\n\n            if (event.metaKey && special !== \"meta\" && modif.indexOf(\"alt+ctrl+shift+\") > -1) {\n                modif = modif.replace(\"alt+ctrl+shift+\", \"hyper+\");\n            }\n\n            if (special) {\n                possible[modif + special] = true;\n            }\n            else {\n                possible[modif + character] = true;\n                possible[modif + jQuery.hotkeys.shiftNums[character]] = true;\n\n                // \"$\" can be triggered as \"Shift+4\" or \"Shift+$\" or just \"$\"\n                if (modif === \"shift+\") {\n                    possible[jQuery.hotkeys.shiftNums[character]] = true;\n                }\n            }\n\n            for (var i = 0,\n                     l = keys.length;\n                 i < l;\n                 i++) {\n                if (possible[keys[i]]) {\n                    return origHandler.apply(this, arguments);\n                }\n            }\n        };\n    }\n\n    jQuery.each([\n        \"keydown\",\n        \"keyup\",\n        \"keypress\"\n    ], function () {\n        jQuery.event.special[this] = {\n            add: keyHandler\n        };\n    });\n\n})(jQuery || this.jQuery || window.jQuery);"
  },
  {
    "path": "src/utils/jquery.ui.touch-punch.js",
    "content": "/*!\n * jQuery UI Touch Punch 0.2.3\n *\n * Copyright 2011–2014, Dave Furfero\n * Dual licensed under the MIT or GPL Version 2 licenses.\n *\n * Depends:\n *  jquery.ui.widget.js\n *  jquery.ui.mouse.js\n */\n(function ($) {\n\n    // Detect touch support\n    $.support.touch = 'ontouchend' in document;\n\n    // Ignore browsers without touch support\n    if (!$.support.touch) {\n        return;\n    }\n\n    var mouseProto = $.ui.mouse.prototype,\n        _mouseInit = mouseProto._mouseInit,\n        _mouseDestroy = mouseProto._mouseDestroy,\n        touchHandled;\n\n    /**\n     * Simulate a mouse event based on a corresponding touch event\n     * @param {Object} event A touch event\n     * @param {String} simulatedType The corresponding mouse event\n     */\n    function simulateMouseEvent(event, simulatedType) {\n\n        // Ignore multi-touch events\n        if (event.originalEvent.touches.length > 1) {\n            return;\n        }\n\n        event.preventDefault();\n\n        var touch = event.originalEvent.changedTouches[0],\n            simulatedEvent = document.createEvent('MouseEvents');\n\n        // Initialize the simulated mouse event using the touch event's coordinates\n        simulatedEvent.initMouseEvent(\n            simulatedType,// type\n            true,// bubbles\n            true,// cancelable\n            window,// view\n            1,// detail\n            touch.screenX,// screenX\n            touch.screenY,// screenY\n            touch.clientX,// clientX\n            touch.clientY,// clientY\n            false,// ctrlKey\n            false,// altKey\n            false,// shiftKey\n            false,// metaKey\n            0,// button\n            null              // relatedTarget\n        );\n\n        // Dispatch the simulated event to the target element\n        event.target.dispatchEvent(simulatedEvent);\n    }\n\n    /**\n     * Handle the jQuery UI widget's touchstart events\n     * @param {Object} event The widget element's touchstart event\n     */\n    mouseProto._touchStart = function (event) {\n\n        var self = this;\n\n        // Ignore the event if another widget is already being handled\n        if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {\n            return;\n        }\n\n        // Set the flag to prevent other widgets from inheriting the touch event\n        touchHandled = true;\n\n        // Track movement to determine if interaction was a click\n        self._touchMoved = false;\n\n        // Simulate the mouseover event\n        simulateMouseEvent(event, 'mouseover');\n\n        // Simulate the mousemove event\n        simulateMouseEvent(event, 'mousemove');\n\n        // Simulate the mousedown event\n        simulateMouseEvent(event, 'mousedown');\n    };\n\n    /**\n     * Handle the jQuery UI widget's touchmove events\n     * @param {Object} event The document's touchmove event\n     */\n    mouseProto._touchMove = function (event) {\n\n        // Ignore event if not handled\n        if (!touchHandled) {\n            return;\n        }\n\n        // Interaction was not a click\n        this._touchMoved = true;\n\n        // Simulate the mousemove event\n        simulateMouseEvent(event, 'mousemove');\n    };\n\n    /**\n     * Handle the jQuery UI widget's touchend events\n     * @param {Object} event The document's touchend event\n     */\n    mouseProto._touchEnd = function (event) {\n\n        // Ignore event if not handled\n        if (!touchHandled) {\n            return;\n        }\n\n        // Simulate the mouseup event\n        simulateMouseEvent(event, 'mouseup');\n\n        // Simulate the mouseout event\n        simulateMouseEvent(event, 'mouseout');\n\n        // If the touch interaction did not move, it should trigger a click\n        if (!this._touchMoved) {\n\n            // Simulate the click event\n            simulateMouseEvent(event, 'click');\n        }\n\n        // Unset the flag to allow other widgets to inherit the touch event\n        touchHandled = false;\n    };\n\n    /**\n     * A duck punch of the $.ui.mouse _mouseInit method to support touch events.\n     * This method extends the widget with bound touch event handlers that\n     * translate touch events to mouse events and pass them to the widget's\n     * original mouse event handling methods.\n     */\n    mouseProto._mouseInit = function () {\n\n        var self = this;\n\n        // Delegate the touch handlers to the widget's element\n        self.element.bind({\n            touchstart: $.proxy(self, '_touchStart'),\n            touchmove: $.proxy(self, '_touchMove'),\n            touchend: $.proxy(self, '_touchEnd')\n        });\n\n        // Call the original $.ui.mouse init method\n        _mouseInit.call(self);\n    };\n\n    /**\n     * Remove the touch event handlers\n     */\n    mouseProto._mouseDestroy = function () {\n\n        var self = this;\n\n        // Delegate the touch handlers to the widget's element\n        self.element.unbind({\n            touchstart: $.proxy(self, '_touchStart'),\n            touchmove: $.proxy(self, '_touchMove'),\n            touchend: $.proxy(self, '_touchEnd')\n        });\n\n        // Call the original $.ui.mouse destroy method\n        _mouseDestroy.call(self);\n    };\n\n})(jQuery);"
  },
  {
    "path": "src/utils/raphael_free_transform.js",
    "content": "/*\n * Licensed under the MIT license:\n * http://www.opensource.org/licenses/mit-license.php\n *\n */\n(function (root, factory) {\n    if (typeof define === 'function' && define.amd) {\n        // AMD. Register as an anonymous module.\n        define(['raphael'], function (Raphael) {\n            // Use global variables if the locals are undefined.\n            return factory(Raphael || root.Raphael);\n        });\n    } else {\n        // RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags\n        factory(Raphael);\n    }\n}(this, function (Raphael) {\n    Raphael.fn.freeTransform = function (subject, options, callback) {\n        var\n            ft, i, mapped, timeout,\n            paper = this,\n            bbox = subject.getBBox(true);\n\n\n        // Enable method chaining\n        if (subject.freeTransform) {\n            return subject.freeTransform;\n        }\n\n        // Add Array.map if the browser doesn't support it\n        if (!Array.prototype.hasOwnProperty('map')) {\n            Array.prototype.map = function (callback, arg) {\n                mapped = [];\n\n                for (i in this) {\n                    if (this.hasOwnProperty(i)) {\n                        mapped[i] = callback.call(arg, this[i], i, this);\n                    }\n                }\n\n                return mapped;\n            };\n        }\n\n        // Add Array.indexOf if not builtin\n        if (!Array.prototype.hasOwnProperty('indexOf')) {\n            Array.prototype.indexOf = function (obj, start) {\n                for (i = ( start || 0 ), j = this.length; i < j; i++) {\n                    if (this[i] === obj) {\n                        return i;\n                    }\n                }\n\n                return -1;\n            };\n        }\n\n        ft = subject.freeTransform = {\n            // Keep track of transformations\n            attrs: {\n                x: bbox.x,\n                y: bbox.y,\n                size: {x: bbox.width, y: bbox.height},\n                center: {x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2},\n                rotate: 0,\n                scale: {x: 1, y: 1},\n                translate: {x: 0, y: 0},\n                ratio: 1\n            },\n            axes: null,\n            bbox: null,\n            callback: null,\n            items: [],\n            handles: {center: null, x: null, y: null},\n            offset: {\n                rotate: 0,\n                scale: {x: 1, y: 1},\n                translate: {x: 0, y: 0}\n            },\n            opts: {\n                animate: false,\n                attrs: {fill: '#fff', stroke: '#000'},\n                boundary: {x: paper._left || 0, y: paper._top || 0, width: null, height: null},\n                customCorners: false, // { size: <number>, distance: <number>, corners: [ action: <string>, image: <string> ]\t}\n                distance: 1.3,\n                drag: true,\n                draw: false,\n                keepRatio: false,\n                range: {rotate: [-180, 180], scale: [-99999, 99999]},\n                rotate: true,\n                scale: true,\n                snap: {rotate: 0, scale: 0, drag: 0},\n                snapDist: {rotate: 0, scale: 0, drag: 7},\n                size: 5\n            },\n            subject: subject\n        };\n\n        /**\n         * Update handles based on the element's transformations\n         */\n        ft.updateHandles = function () {\n            var\n                corners, cornersDistance, rad, radius, cx, cy, viewBoxRatio,\n                bboxHandleDirection = [ // Allowed x, y scaling directions for bbox handles\n                    [-1, -1], [1, -1], [1, 1], [-1, 1],\n                    [0, -1], [1, 0], [0, 1], [-1, 0]\n                ];\n\n            if (ft.handles.bbox || ft.opts.rotate.indexOf('self') >= 0) {\n                cornersDistance = corners = getBBox(0);\n\n                if (ft.opts.customCorners && typeof ft.opts.customCorners.distance === 'number') {\n                    cornersDistance = getBBox(ft.opts.customCorners.distance);\n                }\n            }\n\n            // Get the element's rotation\n            rad = {\n                x: ( ft.attrs.rotate      ) * Math.PI / 180,\n                y: ( ft.attrs.rotate + 90 ) * Math.PI / 180\n            };\n\n            radius = {\n                x: ft.attrs.size.x / 2 * ft.attrs.scale.x,\n                y: ft.attrs.size.y / 2 * ft.attrs.scale.y\n            };\n\n            ft.axes.map(function (axis) {\n                if (ft.handles[axis]) {\n                    cx = ft.attrs.center.x + ft.attrs.translate.x + radius[axis] * ft.opts.distance * Math.cos(rad[axis]),\n                        cy = ft.attrs.center.y + ft.attrs.translate.y + radius[axis] * ft.opts.distance * Math.sin(rad[axis]),\n                        viewBoxRatio = {x: 1, y: 1};\n\n                    // viewBox might be scaled\n                    if (paper._viewBox) {\n                        viewBoxRatio = {\n                            x: paper._viewBox[2] / getPaperSize().x,\n                            y: paper._viewBox[3] / getPaperSize().y\n                        };\n                    }\n\n                    // Keep handle within boundaries\n                    if (ft.opts.boundary) {\n                        cx = Math.max(Math.min(cx, ft.opts.boundary.x + ( ft.opts.boundary.width || getPaperSize().x * viewBoxRatio.x)), ft.opts.boundary.x);\n                        cy = Math.max(Math.min(cy, ft.opts.boundary.y + ( ft.opts.boundary.height || getPaperSize().y * viewBoxRatio.y)), ft.opts.boundary.y);\n                    }\n\n                    ft.handles[axis].disc.attr({cx: cx, cy: cy});\n\n                    ft.handles[axis].line.toFront().attr({\n                        path: [['M', ft.attrs.center.x + ft.attrs.translate.x, ft.attrs.center.y + ft.attrs.translate.y], ['L', ft.handles[axis].disc.attrs.cx, ft.handles[axis].disc.attrs.cy]]\n                    });\n\n                    ft.handles[axis].disc.toFront();\n                }\n            });\n\n            if (ft.bbox) {\n                ft.bbox.toFront().attr({\n                    path: [\n                        ['M', corners[0].x, corners[0].y],\n                        ['L', corners[1].x, corners[1].y],\n                        ['L', corners[2].x, corners[2].y],\n                        ['L', corners[3].x, corners[3].y],\n                        ['L', corners[0].x, corners[0].y]\n                    ]\n                });\n\n                if (ft.handles.bbox) {\n                    ft.handles.bbox.map(function (handle, i) {\n                        var cx, cy, j, k;\n\n                        if (handle.isCorner) {\n                            cx = cornersDistance[i].x;\n                            cy = cornersDistance[i].y;\n                        } else {\n                            j = i % 4;\n                            k = ( j + 1 ) % corners.length;\n                            cx = ( cornersDistance[j].x + cornersDistance[k].x ) / 2;\n                            cy = ( cornersDistance[j].y + cornersDistance[k].y ) / 2;\n                        }\n\n                        handle.element.toFront()\n                            .attr({\n                                x: cx - ( handle.isCorner ? ( ft.opts.customCorners ? Math.ceil(ft.opts.customCorners.size / 2) : ft.opts.size.bboxCorners ) : ft.opts.size.bboxSides ),\n                                y: cy - ( handle.isCorner ? ( ft.opts.customCorners ? Math.ceil(ft.opts.customCorners.size / 2) : ft.opts.size.bboxCorners ) : ft.opts.size.bboxSides )\n                            })\n                            .transform(ft.opts.customCorners ? null : 'R' + ft.attrs.rotate);\n\n                        handle.x = bboxHandleDirection[i][0];\n                        handle.y = bboxHandleDirection[i][1];\n                    });\n                }\n            }\n\n            if (ft.circle) {\n                ft.circle.attr({\n                    cx: ft.attrs.center.x + ft.attrs.translate.x,\n                    cy: ft.attrs.center.y + ft.attrs.translate.y,\n                    r: Math.max(radius.x, radius.y) * ft.opts.distance\n                });\n            }\n\n            if (ft.handles.center) {\n                ft.handles.center.disc.toFront().attr({\n                    cx: ft.attrs.center.x + ft.attrs.translate.x,\n                    cy: ft.attrs.center.y + ft.attrs.translate.y\n                });\n            }\n\n            if (ft.opts.rotate.indexOf('self') >= 0) {\n                radius = Math.max(\n                        Math.sqrt(Math.pow(corners[1].x - corners[0].x, 2) + Math.pow(corners[1].y - corners[0].y, 2)),\n                        Math.sqrt(Math.pow(corners[2].x - corners[1].x, 2) + Math.pow(corners[2].y - corners[1].y, 2))\n                    ) / 2;\n            }\n\n            return ft;\n        };\n\n        /**\n         * Add handles\n         */\n        ft.showHandles = function () {\n            var\n                i, handle, rotate, scale,\n                handles = [];\n            draggables = [];\n\n            ft.hideHandles();\n\n            ft.axes.map(function (axis) {\n                ft.handles[axis] = {};\n\n                ft.handles[axis].line = paper\n                    .path(['M', ft.attrs.center.x, ft.attrs.center.y])\n                    .attr({\n                        stroke: ft.opts.attrs.stroke,\n                        'stroke-dasharray': '- ',\n                        opacity: .5\n                    });\n\n                ft.handles[axis].disc = paper\n                    .circle(ft.attrs.center.x, ft.attrs.center.y, ft.opts.size.axes)\n                    .attr(ft.opts.attrs);\n            });\n\n            if (ft.opts.draw.indexOf('bbox') >= 0) {\n                ft.bbox = paper\n                    .path('')\n                    .attr({\n                        stroke: ft.opts.attrs.stroke,\n                        'stroke-dasharray': '- ',\n                        opacity: .5\n                    });\n\n                ft.handles.bbox = [];\n\n                for (i = ( ft.opts.scale.indexOf('bboxCorners') || ft.opts.customCorners >= 0 ? 0 : 4 ); i < ( ft.opts.scale.indexOf('bboxSides') === -1 ? 4 : 8 ); i++) {\n                    handle = {};\n\n                    handle.index = i;\n                    handle.axis = i % 2 ? 'x' : 'y';\n                    handle.isCorner = i < 4;\n\n                    if (handle.isCorner && ft.opts.customCorners) {\n                        handle.element = paper\n                            .image(ft.opts.customCorners.corners[i].image, ft.attrs.center.x, ft.attrs.center.y, ft.opts.customCorners.size, ft.opts.customCorners.size);\n                    } else {\n                        handle.element = paper\n                            .rect(ft.attrs.center.x, ft.attrs.center.y, ft.opts.size[handle.isCorner ? 'bboxCorners' : 'bboxSides'] * 2, ft.opts.size[handle.isCorner ? 'bboxCorners' : 'bboxSides'] * 2)\n                            .attr(ft.opts.attrs);\n                    }\n\n                    ft.handles.bbox[i] = handle;\n                }\n            }\n\n            if (ft.opts.draw.indexOf('circle') !== -1) {\n                ft.circle = paper\n                    .circle(0, 0, 0)\n                    .attr({\n                        stroke: ft.opts.attrs.stroke,\n                        'stroke-dasharray': '- ',\n                        opacity: .3\n                    });\n            }\n\n            if (ft.opts.drag.indexOf('center') !== -1) {\n                ft.handles.center = {};\n\n                ft.handles.center.disc = paper\n                    .circle(ft.attrs.center.x, ft.attrs.center.y, ft.opts.size.center)\n                    .attr(ft.opts.attrs);\n            }\n\n            // Drag x, y handles and custom corners\n            ft.axes.map(function (axis) {\n                if (ft.handles[axis]) {\n                    handles.push({\n                        element: ft.handles[axis].disc,\n                        rotate: ft.opts.rotate.indexOf('axis' + axis.toUpperCase()) !== -1,\n                        scale: ft.opts.scale.indexOf('axis' + axis.toUpperCase()) !== -1,\n                        axis: axis,\n                        isCorner: false\n                    });\n                }\n            });\n\n            if (ft.opts.customCorners) {\n                ft.handles.bbox.map(function (handle) {\n                    var action;\n\n                    if (typeof ft.opts.customCorners.corners[handle.index] !== 'undefined') {\n                        action = ft.opts.customCorners.corners[handle.index].action;\n\n                        if (action === 'rotate' || action === 'scale') {\n                            handles.push({\n                                element: handle.element,\n                                rotate: action === 'rotate',\n                                scale: action === 'scale',\n                                axis: 'x',\n                                isCorner: true\n                            });\n                        }\n                    }\n                });\n            }\n\n            handles.map(function (handle) {\n                handle.element.drag(function (dx, dy) {\n                    var\n                        cx, cy, rad, opposite, adjacent, hypotenuse, pos,\n                        mirrored = {\n                            x: ft.o.scale.x < 0,\n                            y: ft.o.scale.y < 0\n                        };\n\n                    // viewBox might be scaled\n                    if (ft.o.viewBoxRatio) {\n                        dx *= ft.o.viewBoxRatio.x;\n                        dy *= ft.o.viewBoxRatio.y;\n                    }\n\n                    cx = dx + handle.element.ox,\n                        cy = dy + handle.element.oy;\n\n                    if (handle.rotate) {\n                        rad = Math.atan2(cy - ft.o.center.y - ft.o.translate.y, cx - ft.o.center.x - ft.o.translate.x);\n\n                        ft.attrs.rotate = rad * 180 / Math.PI - ft.o.angle;\n\n                        if (mirrored[handle.axis]) {\n                            ft.attrs.rotate -= 180;\n                        }\n                    }\n\n                    // Keep handle within boundaries\n                    if (ft.opts.boundary) {\n                        cx = Math.max(Math.min(cx, ft.opts.boundary.x + ( ft.opts.boundary.width || getPaperSize().x * (ft.o.viewBoxRatio && ft.o.viewBoxRatio.x || 1))), ft.opts.boundary.x);\n                        cy = Math.max(Math.min(cy, ft.opts.boundary.y + ( ft.opts.boundary.height || getPaperSize().y * (ft.o.viewBoxRatio && ft.o.viewBoxRatio.y || 1))), ft.opts.boundary.y);\n                    }\n\n                    opposite = cx - ft.o.center.x - ft.o.translate.x - ( ft.opts.customCorners ? ft.opts.customCorners.distance : 0 ),\n                        adjacent = cy - ft.o.center.y - ft.o.translate.y - ( ft.opts.customCorners ? ft.opts.customCorners.distance : 0 ),\n                        hypotenuse = Math.sqrt(Math.pow(opposite, 2) + Math.pow(adjacent, 2));\n\n                    if (handle.scale) {\n                        ft.attrs.scale[handle.axis] = hypotenuse / ( ft.o.size[handle.axis] / 2 * ft.o.distance );\n\n                        if (mirrored[handle.axis]) {\n                            ft.attrs.scale[handle.axis] *= -1;\n                        }\n                    }\n\n                    applyLimits();\n\n                    // Maintain aspect ratio\n                    if (ft.opts.keepRatio.indexOf('axis' + handle.axis.toUpperCase()) !== -1) {\n                        keepRatio(handle.axis);\n                    } else {\n                        ft.attrs.ratio = ft.attrs.scale.x / ft.attrs.scale.y;\n                    }\n\n                    if (ft.attrs.scale.x && ft.attrs.scale.y) {\n                        ft.apply();\n                    }\n\n                    asyncCallback([handle.rotate ? 'rotate' : null, handle.scale ? 'scale' : null]);\n                }, function () {\n                    // Offset values\n                    ft.o = cloneObj(ft.attrs);\n\n                    if (paper._viewBox) {\n                        ft.o.viewBoxRatio = {\n                            x: paper._viewBox[2] / getPaperSize().x,\n                            y: paper._viewBox[3] / getPaperSize().y\n                        };\n                    }\n\n                    handle.element.ox = this.attrs.cx ? this.attrs.cx : this.attrs.x + this.attrs.width / 2;\n                    handle.element.oy = this.attrs.cy ? this.attrs.cy : this.attrs.y + this.attrs.height / 2;\n\n                    if (handle.isCorner) {\n                        hypotenuse = Math.sqrt(Math.pow(ft.o.size.x * ft.o.scale.x, 2) + Math.pow(ft.o.size.y * ft.o.scale.y, 2)) / 2,\n                            opposite = ft.o.size.x * ft.o.scale.x / 2,\n                            pos = {\n                                x: handle.element.ox - ( ft.o.center.x + ft.o.x + ft.o.translate.x ),\n                                y: handle.element.oy - ( ft.o.center.y + ft.o.y + ft.o.translate.y )\n                            };\n\n                        // Distance from centre of handle to centre of element\n                        ft.o.distance = hypotenuse / opposite;\n\n                        ft.o.angle = 90 - ( Math.atan2(pos.x, pos.y) * 180 / Math.PI ) - ft.o.rotate;\n                    } else {\n                        ft.o.distance = ft.opts.distance;\n                        ft.o.angle = handle.axis === 'y' ? 90 : 0;\n                    }\n\n                    asyncCallback([handle.rotate ? 'rotate start' : null, handle.scale ? 'scale start' : null]);\n                }, function () {\n                    asyncCallback([handle.rotate ? 'rotate end' : null, handle.scale ? 'scale end' : null]);\n                });\n            });\n\n            // Drag bbox handles\n            if (ft.opts.draw.indexOf('bbox') >= 0 && !ft.opts.customCorners && ( ft.opts.scale.indexOf('bboxCorners') !== -1 || ft.opts.scale.indexOf('bboxSides') !== -1 )) {\n                ft.handles.bbox.map(function (handle) {\n                    handle.element.drag(function (dx, dy) {\n                        var\n                            sin, cos, rx, ry, rdx, rdy, mx, my, sx, sy, ratio, tdy, tdx,\n                            previous = cloneObj(ft.attrs),\n                            trans = {x: 0, y: 0};\n\n                        // viewBox might be scaled\n                        if (ft.o.viewBoxRatio) {\n                            dx *= ft.o.viewBoxRatio.x;\n                            dy *= ft.o.viewBoxRatio.y;\n                        }\n\n                        sin = ft.o.rotate.sin;\n                        cos = ft.o.rotate.cos;\n\n                        // First rotate dx, dy to element alignment\n                        rx = dx * cos - dy * sin;\n                        ry = dx * sin + dy * cos;\n\n                        rx *= Math.abs(handle.x);\n                        ry *= Math.abs(handle.y);\n\n                        // And finally rotate back to canvas alignment\n                        rdx = rx * cos + ry * sin;\n                        rdy = rx * -sin + ry * cos;\n\n                        ft.attrs.translate = {\n                            x: ft.o.translate.x + rdx / 2,\n                            y: ft.o.translate.y + rdy / 2\n                        };\n\n                        // Mouse position, relative to element center after translation\n                        mx = ft.o.handlePos.cx + dx - ft.attrs.center.x - ft.attrs.translate.x;\n                        my = ft.o.handlePos.cy + dy - ft.attrs.center.y - ft.attrs.translate.y;\n\n                        // Position rotated to align with element\n                        rx = mx * cos - my * sin;\n                        ry = mx * sin + my * cos;\n\n                        // Maintain aspect ratio\n                        if (handle.isCorner && ft.opts.keepRatio.indexOf('bboxCorners') !== -1) {\n                            ratio = ( ft.attrs.size.x * ft.attrs.scale.x ) / ( ft.attrs.size.y * ft.attrs.scale.y ),\n                                tdy = rx * handle.x * ( 1 / ratio ),\n                                tdx = ry * handle.y * ratio;\n\n                            if (tdx > tdy * ratio) {\n                                rx = tdx * handle.x;\n                            } else {\n                                ry = tdy * handle.y;\n                            }\n                        }\n\n                        // Scale element so that handle is at mouse position\n                        sx = rx * 2 * handle.x / ft.o.size.x;\n                        sy = ry * 2 * handle.y / ft.o.size.y;\n\n                        ft.attrs.scale = {\n                            x: sx || ft.attrs.scale.x,\n                            y: sy || ft.attrs.scale.y\n                        };\n\n                        // Check boundaries\n                        if (!isWithinBoundaries().x || !isWithinBoundaries().y) {\n                            ft.attrs = previous;\n                        }\n\n                        applyLimits();\n\n                        // Maintain aspect ratio\n                        if (( handle.isCorner && ft.opts.keepRatio.indexOf('bboxCorners') !== -1 ) || ( !handle.isCorner && ft.opts.keepRatio.indexOf('bboxSides') !== -1 )) {\n                            keepRatio(handle.axis);\n\n                            trans.x = ( ft.attrs.scale.x - ft.o.scale.x ) * ft.o.size.x * handle.x;\n                            trans.y = ( ft.attrs.scale.y - ft.o.scale.y ) * ft.o.size.y * handle.y;\n\n                            rx = trans.x * cos + trans.y * sin;\n                            ry = -trans.x * sin + trans.y * cos;\n\n                            ft.attrs.translate.x = ft.o.translate.x + rx / 2;\n                            ft.attrs.translate.y = ft.o.translate.y + ry / 2;\n                        }\n\n                        ft.attrs.ratio = ft.attrs.scale.x / ft.attrs.scale.y;\n\n                        asyncCallback(['scale']);\n\n                        ft.apply();\n                    }, function () {\n                        var\n                            angle = ( ( 360 - ft.attrs.rotate ) % 360 ) / 180 * Math.PI,\n                            handlePos = handle.element.attr(['x', 'y']);\n\n                        // Offset values\n                        ft.o = cloneObj(ft.attrs);\n\n                        ft.o.handlePos = {\n                            cx: handlePos.x + ft.opts.size[handle.isCorner ? 'bboxCorners' : 'bboxSides'],\n                            cy: handlePos.y + ft.opts.size[handle.isCorner ? 'bboxCorners' : 'bboxSides']\n                        };\n\n                        // Pre-compute rotation sin & cos for efficiency\n                        ft.o.rotate = {\n                            sin: Math.sin(angle),\n                            cos: Math.cos(angle)\n                        };\n\n                        if (paper._viewBox) {\n                            ft.o.viewBoxRatio = {\n                                x: paper._viewBox[2] / getPaperSize().x,\n                                y: paper._viewBox[3] / getPaperSize().y\n                            };\n                        }\n\n                        asyncCallback(['scale start']);\n                    }, function () {\n                        asyncCallback(['scale end']);\n                    });\n                });\n            }\n\n            // Custom BBox corners\n            if (ft.opts.customCorners) {\n                ft.handles.bbox.map(function (handle) {\n                    if (typeof ft.opts.customCorners.corners[handle.index] !== 'undefined') {\n                        if (['drag', 'rotate', 'scale'].indexOf(ft.opts.customCorners.corners[handle.index].action) === -1) {\n                            // Unknown action, absorb drag event and emit as custom event on click\n                            handle.element.drag(function () {\n                                return false;\n                            });\n\n                            handle.element.click(function () {\n                                asyncCallback(['custom:' + ft.opts.customCorners.corners[handle.index].action]);\n                            });\n                        }\n                    }\n                });\n            }\n\n            // Drag element and center handle\n            if (ft.opts.drag.indexOf('self') >= 0 && ft.opts.scale.indexOf('self') === -1 && ft.opts.rotate.indexOf('self') === -1) {\n                draggables.push(subject);\n            }\n\n            if (ft.opts.drag.indexOf('center') >= 0) {\n                draggables.push(ft.handles.center.disc);\n            }\n\n            if (ft.opts.customCorners) {\n                ft.handles.bbox.map(function (handle) {\n                    if (typeof ft.opts.customCorners.corners[handle.index] !== 'undefined') {\n                        if (ft.opts.customCorners.corners[handle.index].action === 'drag') {\n                            draggables.push(handle.element);\n                        }\n                    }\n                });\n            }\n\n            draggables.map(function (draggable) {\n                draggable.drag(function (dx, dy) {\n                    var bbox = cloneObj(ft.o.bbox);\n\n                    // viewBox might be scaled\n                    if (ft.o.viewBoxRatio) {\n                        dx *= ft.o.viewBoxRatio.x;\n                        dy *= ft.o.viewBoxRatio.y;\n                    }\n\n                    ft.attrs.translate.x = ft.o.translate.x + dx;\n                    ft.attrs.translate.y = ft.o.translate.y + dy;\n\n                    bbox.x += dx;\n                    bbox.y += dy;\n\n                    applyLimits(bbox);\n\n                    asyncCallback(['drag']);\n\n                    ft.apply();\n                }, function () {\n                    // Offset values\n                    ft.o = cloneObj(ft.attrs);\n\n                    if (ft.opts.snap.drag) {\n                        ft.o.bbox = subject.getBBox();\n                    }\n\n                    // viewBox might be scaled\n                    if (paper._viewBox) {\n                        ft.o.viewBoxRatio = {\n                            x: paper._viewBox[2] / getPaperSize().x,\n                            y: paper._viewBox[3] / getPaperSize().y\n                        };\n                    }\n\n                    ft.axes.map(function (axis) {\n                        if (ft.handles[axis]) {\n                            ft.handles[axis].disc.ox = ft.handles[axis].disc.attrs.cx;\n                            ft.handles[axis].disc.oy = ft.handles[axis].disc.attrs.cy;\n                        }\n                    });\n\n                    asyncCallback(['drag start']);\n                }, function () {\n                    asyncCallback(['drag end']);\n                });\n            });\n\n            rotate = ft.opts.rotate.indexOf('self') >= 0,\n                scale = ft.opts.scale.indexOf('self') >= 0;\n\n            if (rotate || scale) {\n                subject.drag(function (dx, dy, x, y) {\n                    var\n                        rad, radius,\n                        mirrored = {\n                            x: ft.o.scale.x < 0,\n                            y: ft.o.scale.y < 0\n                        };\n\n                    if (rotate) {\n                        rad = Math.atan2(y - ft.o.center.y - ft.o.translate.y, x - ft.o.center.x - ft.o.translate.x);\n\n                        ft.attrs.rotate = ft.o.rotate + ( rad * 180 / Math.PI ) - ft.o.deg;\n                    }\n\n                    if (scale) {\n                        radius = Math.sqrt(Math.pow(x - ft.o.center.x - ft.o.translate.x, 2) + Math.pow(y - ft.o.center.y - ft.o.translate.y, 2));\n\n                        ft.attrs.scale.x = ft.attrs.scale.y = ( mirrored.x ? -1 : 1 ) * ft.o.scale.x + ( radius - ft.o.radius ) / ( ft.o.size.x / 2 );\n\n                        if (mirrored.x) {\n                            ft.attrs.scale.x *= -1;\n                        }\n                        if (mirrored.y) {\n                            ft.attrs.scale.y *= -1;\n                        }\n                    }\n\n                    applyLimits();\n\n                    ft.apply();\n\n                    asyncCallback([rotate ? 'rotate' : null, scale ? 'scale' : null]);\n                }, function (x, y) {\n                    // Offset values\n                    ft.o = cloneObj(ft.attrs);\n\n                    ft.o.deg = Math.atan2(y - ft.o.center.y - ft.o.translate.y, x - ft.o.center.x - ft.o.translate.x) * 180 / Math.PI;\n\n                    ft.o.radius = Math.sqrt(Math.pow(x - ft.o.center.x - ft.o.translate.x, 2) + Math.pow(y - ft.o.center.y - ft.o.translate.y, 2));\n\n                    // viewBox might be scaled\n                    if (paper._viewBox) {\n                        ft.o.viewBoxRatio = {\n                            x: paper._viewBox[2] / getPaperSize().x,\n                            y: paper._viewBox[3] / getPaperSize().y\n                        };\n                    }\n\n                    asyncCallback([rotate ? 'rotate start' : null, scale ? 'scale start' : null]);\n                }, function () {\n                    asyncCallback([rotate ? 'rotate end' : null, scale ? 'scale end' : null]);\n                });\n            }\n\n            ft.updateHandles();\n\n            return ft;\n        };\n\n        /**\n         * Remove handles\n         */\n        ft.hideHandles = function (opts) {\n            var opts = opts || {}\n\n            if (opts.undrag === undefined) {\n                opts.undrag = true;\n            }\n\n            if (opts.undrag) {\n                ft.items.map(function (item) {\n                    item.el.undrag();\n                });\n            }\n\n            if (ft.handles.center) {\n                ft.handles.center.disc.remove();\n\n                ft.handles.center = null;\n            }\n\n            ['x', 'y'].map(function (axis) {\n                if (ft.handles[axis]) {\n                    ft.handles[axis].disc.remove();\n                    ft.handles[axis].line.remove();\n\n                    ft.handles[axis] = null;\n                }\n            });\n\n            if (ft.bbox) {\n                ft.bbox.remove();\n\n                ft.bbox = null;\n\n                if (ft.handles.bbox) {\n                    ft.handles.bbox.map(function (handle) {\n                        handle.element.remove();\n                    });\n\n                    ft.handles.bbox = null;\n                }\n            }\n\n            if (ft.circle) {\n                ft.circle.remove();\n\n                ft.circle = null;\n            }\n\n            return ft;\n        };\n\n        // Override defaults\n        ft.setOpts = function (options, callback) {\n            var i, j;\n\n            if (callback !== undefined) {\n                ft.callback = typeof callback === 'function' ? callback : false;\n            }\n\n            for (i in options) {\n                if (options[i] && options[i].constructor === Object) {\n                    if (ft.opts[i] === false) {\n                        ft.opts[i] = {};\n                    }\n                    for (j in options[i]) {\n                        if (options[i].hasOwnProperty(j)) {\n                            ft.opts[i][j] = options[i][j];\n                        }\n                    }\n                } else {\n                    ft.opts[i] = options[i];\n                }\n            }\n\n            if (ft.opts.animate === true) {\n                ft.opts.animate = {delay: 700, easing: 'linear'};\n            }\n\n            if (ft.opts.drag === true) {\n                ft.opts.drag = ['center', 'self'];\n            }\n\n            if (ft.opts.keepRatio === true) {\n                ft.opts.keepRatio = ['axisX', 'axisY', 'bboxCorners', 'bboxSides'];\n            }\n\n            if (ft.opts.rotate === true) {\n                ft.opts.rotate = ['axisX', 'axisY'];\n            }\n\n            if (ft.opts.scale === true) {\n                ft.opts.scale = ['axisX', 'axisY', 'bboxCorners', 'bboxSides'];\n            }\n\n            ['drag', 'draw', 'keepRatio', 'rotate', 'scale'].map(function (option) {\n                if (ft.opts[option] === false) {\n                    ft.opts[option] = [];\n                }\n            });\n\n            ft.axes = [];\n\n            if (ft.opts.rotate.indexOf('axisX') >= 0 || ft.opts.scale.indexOf('axisX') >= 0) {\n                ft.axes.push('x');\n            }\n\n            if (ft.opts.rotate.indexOf('axisY') >= 0 || ft.opts.scale.indexOf('axisY') >= 0) {\n                ft.axes.push('y');\n            }\n\n            ['drag', 'rotate', 'scale'].map(function (option) {\n                if (!ft.opts.snapDist[option]) {\n                    ft.opts.snapDist[option] = ft.opts.snap[option];\n                }\n            });\n\n            // Force numbers\n            ft.opts.range = {\n                rotate: [parseFloat(ft.opts.range.rotate[0]), parseFloat(ft.opts.range.rotate[1])],\n                scale: [parseFloat(ft.opts.range.scale[0]), parseFloat(ft.opts.range.scale[1])]\n            };\n\n            ft.opts.snap = {\n                drag: parseFloat(ft.opts.snap.drag) || 0,\n                rotate: parseFloat(ft.opts.snap.rotate) || 0,\n                scale: parseFloat(ft.opts.snap.scale) || 0\n            };\n\n            ft.opts.snapDist = {\n                drag: parseFloat(ft.opts.snapDist.drag) || 0,\n                rotate: parseFloat(ft.opts.snapDist.rotate) || 0,\n                scale: parseFloat(ft.opts.snapDist.scale) || 0\n            };\n\n            if (typeof ft.opts.size === 'string') {\n                ft.opts.size = parseFloat(ft.opts.size) || 0;\n            }\n\n            if (!isNaN(ft.opts.size)) {\n                ft.opts.size = {\n                    axes: ft.opts.size,\n                    bboxCorners: ft.opts.size,\n                    bboxSides: ft.opts.size,\n                    center: ft.opts.size\n                };\n            }\n\n            if (ft.opts.customCorners) {\n                ft.opts.customCorners.size = parseFloat(ft.opts.customCorners.size) || 0;\n                ft.opts.customCorners.distance = parseFloat(ft.opts.customCorners.distance) || 0;\n            }\n\n            ft.showHandles();\n\n            asyncCallback(['init']);\n\n            return ft;\n        };\n\n        ft.setOpts(options, callback);\n\n        /**\n         * Apply transformations, optionally update attributes manually\n         */\n        ft.apply = function () {\n            ft.items.map(function (item, i) {\n                // Take offset values into account\n                var\n                    center = {\n                        x: ft.attrs.center.x + ft.offset.translate.x,\n                        y: ft.attrs.center.y + ft.offset.translate.y\n                    },\n                    rotate = ft.attrs.rotate - ft.offset.rotate,\n                    scale = {\n                        x: ft.attrs.scale.x / ft.offset.scale.x,\n                        y: ft.attrs.scale.y / ft.offset.scale.y\n                    },\n                    translate = {\n                        x: ft.attrs.translate.x - ft.offset.translate.x,\n                        y: ft.attrs.translate.y - ft.offset.translate.y\n                    };\n\n                if (ft.opts.animate) {\n                    asyncCallback(['animate start']);\n\n                    item.el.animate(\n                        {\n                            transform: [\n                                'R', rotate, center.x, center.y,\n                                'S', scale.x, scale.y, center.x, center.y,\n                                'T', translate.x, translate.y\n                            ] + ft.items[i].transformString\n                        },\n                        ft.opts.animate.delay,\n                        ft.opts.animate.easing,\n                        function () {\n                            asyncCallback(['animate end']);\n\n                            ft.updateHandles();\n                        }\n                    );\n                } else {\n                    item.el.transform([\n                            'R', rotate, center.x, center.y,\n                            'S', scale.x, scale.y, center.x, center.y,\n                            'T', translate.x, translate.y\n                        ] + ft.items[i].transformString);\n\n                    asyncCallback(['apply']);\n\n                    ft.updateHandles();\n                }\n            });\n\n            return ft;\n        };\n\n        /**\n         * Clean exit\n         */\n        ft.unplug = function () {\n            var attrs = ft.attrs;\n\n            ft.hideHandles();\n\n            // Goodbye\n            delete subject.freeTransform;\n\n            return attrs;\n        };\n\n        // Store attributes for each item\n        function scan(subject) {\n            ( subject.type === 'set' ? subject.items : [subject] ).map(function (item) {\n                if (item.type === 'set') {\n                    scan(item);\n                } else {\n                    ft.items.push({\n                        el: item,\n                        attrs: {\n                            rotate: 0,\n                            scale: {x: 1, y: 1},\n                            translate: {x: 0, y: 0}\n                        },\n                        transformString: item.matrix.toTransformString()\n                    });\n                }\n            });\n        }\n\n        scan(subject);\n\n        // Get the current transform values for each item\n        ft.items.map(function (item, i) {\n            if (item.el._ && item.el._.transform && typeof item.el._.transform === 'object') {\n                item.el._.transform.map(function (transform) {\n                    if (transform[0]) {\n                        switch (transform[0].toUpperCase()) {\n                            case 'T':\n                                ft.items[i].attrs.translate.x += transform[1];\n                                ft.items[i].attrs.translate.y += transform[2];\n\n                                break;\n                            case 'S':\n                                ft.items[i].attrs.scale.x *= transform[1];\n                                ft.items[i].attrs.scale.y *= transform[2];\n\n                                break;\n                            case 'R':\n                                ft.items[i].attrs.rotate += transform[1];\n\n                                break;\n                        }\n                    }\n                });\n            }\n        });\n\n        // If subject is not of type set, the first item _is_ the subject\n        if (subject.type !== 'set') {\n            ft.attrs.rotate = ft.items[0].attrs.rotate;\n            ft.attrs.scale = ft.items[0].attrs.scale;\n            ft.attrs.translate = ft.items[0].attrs.translate;\n\n            ft.items[0].attrs = {\n                rotate: 0,\n                scale: {x: 1, y: 1},\n                translate: {x: 0, y: 0}\n            };\n\n            ft.items[0].transformString = '';\n        }\n\n        ft.attrs.ratio = ft.attrs.scale.x / ft.attrs.scale.y;\n\n        /**\n         * Get rotated bounding box\n         */\n        function getBBox(distance) {\n            var\n                rad, radius,\n                corners = [],\n                signs = [{x: -1, y: -1}, {x: 1, y: -1}, {x: 1, y: 1}, {x: -1, y: 1}];\n\n            if (typeof distance !== 'number') {\n                distance = 0;\n            }\n\n            rad = {\n                x: ( ft.attrs.rotate      ) * Math.PI / 180,\n                y: ( ft.attrs.rotate + 90 ) * Math.PI / 180\n            };\n\n            radius = {\n                x: ft.attrs.size.x / 2 * ft.attrs.scale.x + distance,\n                y: ft.attrs.size.y / 2 * ft.attrs.scale.y + distance\n            };\n\n            signs.map(function (sign) {\n                corners.push({\n                    x: ( ft.attrs.center.x + ft.attrs.translate.x + sign.x * radius.x * Math.cos(rad.x) ) + sign.y * radius.y * Math.cos(rad.y),\n                    y: ( ft.attrs.center.y + ft.attrs.translate.y + sign.x * radius.x * Math.sin(rad.x) ) + sign.y * radius.y * Math.sin(rad.y)\n                });\n            });\n\n            return corners;\n        }\n\n        /**\n         * Get dimension of the paper\n         */\n        function getPaperSize() {\n            var match = {\n                x: /^([0-9]+)%$/.exec(paper.width),\n                y: /^([0-9]+)%$/.exec(paper.height)\n            };\n\n            return {\n                x: match.x ? paper.canvas.clientWidth || paper.canvas.parentNode.clientWidth * parseInt(match.x[1], 10) * 0.01 : paper.canvas.clientWidth || paper.width,\n                y: match.y ? paper.canvas.clientHeight || paper.canvas.parentNode.clientHeight * parseInt(match.y[1], 10) * 0.01 : paper.canvas.clientHeight || paper.height\n            };\n        }\n\n        /**\n         * Apply limits\n         */\n        function applyLimits(bbox) {\n            var\n                x, y, deg,\n                dist = {x: 0, y: 0},\n                snap = {x: 0, y: 0};\n\n            // Snap to grid\n            if (bbox && ft.opts.snap.drag) {\n                x = bbox.x,\n                    y = bbox.y,\n\n                    [0, 1].map(function () {\n                        // Top and left sides first\n                        dist.x = x - Math.round(x / ft.opts.snap.drag) * ft.opts.snap.drag;\n                        dist.y = y - Math.round(y / ft.opts.snap.drag) * ft.opts.snap.drag;\n\n                        if (Math.abs(dist.x) <= ft.opts.snapDist.drag) {\n                            snap.x = dist.x;\n                        }\n                        if (Math.abs(dist.y) <= ft.opts.snapDist.drag) {\n                            snap.y = dist.y;\n                        }\n\n                        // Repeat for bottom and right sides\n                        x += bbox.width - snap.x;\n                        y += bbox.height - snap.y;\n                    });\n\n                ft.attrs.translate.x -= snap.x;\n                ft.attrs.translate.y -= snap.y;\n            }\n\n            // Keep center within boundaries\n            if (ft.opts.boundary) {\n                ft.opts.boundarywidth = ft.opts.boundarywidth || getPaperSize().x * (ft.o.viewBoxRatio && ft.o.viewBoxRatio.x || 1);\n                ft.opts.boundaryheight = ft.opts.boundaryheight || getPaperSize().y * (ft.o.viewBoxRatio && ft.o.viewBoxRatio.y || 1);\n\n                if (ft.attrs.center.x + ft.attrs.translate.x < ft.opts.boundaryx) {\n                    ft.attrs.translate.x += ft.opts.boundaryx - ( ft.attrs.center.x + ft.attrs.translate.x );\n                }\n\n                if (ft.attrs.center.y + ft.attrs.translate.y < ft.opts.boundaryy) {\n                    ft.attrs.translate.y += ft.opts.boundaryy - ( ft.attrs.center.y + ft.attrs.translate.y );\n                }\n\n                if (ft.attrs.center.x + ft.attrs.translate.x > ft.opts.boundaryx + ft.opts.boundarywidth) {\n                    ft.attrs.translate.x += ft.opts.boundaryx + ft.opts.boundarywidth - ( ft.attrs.center.x + ft.attrs.translate.x );\n                }\n\n                if (ft.attrs.center.y + ft.attrs.translate.y > ft.opts.boundaryy + ft.opts.boundaryheight) {\n                    ft.attrs.translate.y += ft.opts.boundaryy + ft.opts.boundaryheight - ( ft.attrs.center.y + ft.attrs.translate.y );\n                }\n            }\n\n            // Snap to angle, rotate with increments\n            dist = Math.abs(ft.attrs.rotate % ft.opts.snap.rotate);\n            dist = Math.min(dist, ft.opts.snap.rotate - dist);\n\n            if (dist < ft.opts.snapDist.rotate) {\n                ft.attrs.rotate = Math.round(ft.attrs.rotate / ft.opts.snap.rotate) * ft.opts.snap.rotate;\n            }\n\n            // Snap to scale, scale with increments\n            dist = {\n                x: Math.abs(( ft.attrs.scale.x * ft.attrs.size.x ) % ft.opts.snap.scale),\n                y: Math.abs(( ft.attrs.scale.y * ft.attrs.size.x ) % ft.opts.snap.scale)\n            };\n\n            dist = {\n                x: Math.min(dist.x, ft.opts.snap.scale - dist.x),\n                y: Math.min(dist.y, ft.opts.snap.scale - dist.y)\n            };\n\n            if (dist.x < ft.opts.snapDist.scale) {\n                ft.attrs.scale.x = Math.round(ft.attrs.scale.x * ft.attrs.size.x / ft.opts.snap.scale) * ft.opts.snap.scale / ft.attrs.size.x;\n            }\n\n            if (dist.y < ft.opts.snapDist.scale) {\n                ft.attrs.scale.y = Math.round(ft.attrs.scale.y * ft.attrs.size.y / ft.opts.snap.scale) * ft.opts.snap.scale / ft.attrs.size.y;\n            }\n\n            // Limit range of rotation\n            if (ft.opts.range.rotate) {\n                deg = ( 360 + ft.attrs.rotate ) % 360;\n\n                if (deg > 180) {\n                    deg -= 360;\n                }\n\n                if (deg < ft.opts.range.rotate[0]) {\n                    ft.attrs.rotate += ft.opts.range.rotate[0] - deg;\n                }\n                if (deg > ft.opts.range.rotate[1]) {\n                    ft.attrs.rotate += ft.opts.range.rotate[1] - deg;\n                }\n            }\n\n            // Limit scale\n            if (ft.opts.range.scale) {\n                if (ft.attrs.scale.x * ft.attrs.size.x < ft.opts.range.scale[0]) {\n                    ft.attrs.scale.x = ft.opts.range.scale[0] / ft.attrs.size.x;\n                }\n\n                if (ft.attrs.scale.y * ft.attrs.size.y < ft.opts.range.scale[0]) {\n                    ft.attrs.scale.y = ft.opts.range.scale[0] / ft.attrs.size.y;\n                }\n\n                if (ft.attrs.scale.x * ft.attrs.size.x > ft.opts.range.scale[1]) {\n                    ft.attrs.scale.x = ft.opts.range.scale[1] / ft.attrs.size.x;\n                }\n\n                if (ft.attrs.scale.y * ft.attrs.size.y > ft.opts.range.scale[1]) {\n                    ft.attrs.scale.y = ft.opts.range.scale[1] / ft.attrs.size.y;\n                }\n            }\n        }\n\n        function isWithinBoundaries() {\n            return {\n                x: ft.attrs.scale.x * ft.attrs.size.x >= ft.opts.range.scale[0] && ft.attrs.scale.x * ft.attrs.size.x <= ft.opts.range.scale[1],\n                y: ft.attrs.scale.y * ft.attrs.size.y >= ft.opts.range.scale[0] && ft.attrs.scale.y * ft.attrs.size.y <= ft.opts.range.scale[1]\n            };\n        }\n\n        function keepRatio(axis) {\n            if (axis === 'x') {\n                ft.attrs.scale.y = ft.attrs.scale.x / ft.attrs.ratio;\n            } else {\n                ft.attrs.scale.x = ft.attrs.scale.y * ft.attrs.ratio;\n            }\n        }\n\n        /**\n         * Recursive copy of object\n         */\n        function cloneObj(obj) {\n            var i, clone = {};\n\n            for (i in obj) {\n                clone[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i];\n            }\n\n            return clone;\n        }\n\n        /**\n         * Call callback asynchronously for better performance\n         */\n        function asyncCallback(e) {\n            var events = [];\n\n            if (ft.callback) {\n                // Remove empty values\n                e.map(function (e, i) {\n                    if (e) {\n                        events.push(e);\n                    }\n                });\n\n                clearTimeout(timeout);\n\n                timeout = setTimeout(function () {\n                    if (ft.callback) {\n                        ft.callback(ft, events, getBBox());\n                    }\n                }, .1);\n            }\n        }\n\n        ft.updateHandles();\n\n        // Enable method chaining\n        return ft;\n    };\n}));"
  },
  {
    "path": "tests/.jshintrc",
    "content": "{\n\t\"curly\": true,\n\t\"eqeqeq\": true,\n\t\"immed\": true,\n\t\"latedef\": true,\n\t\"newcap\": true,\n\t\"noarg\": true,\n\t\"sub\": true,\n\t\"undef\": true,\n\t\"unused\": true,\n\t\"boss\": true,\n\t\"eqnull\": true,\n\t\"browser\": true,\n\t\"predef\": [\n\t\t\"jQuery\",\n\t\t\"QUnit\",\n\t\t\"module\",\n\t\t\"test\",\n\t\t\"asyncTest\",\n\t\t\"expect\",\n\t\t\"start\",\n\t\t\"stop\",\n\t\t\"ok\",\n\t\t\"equal\",\n\t\t\"notEqual\",\n\t\t\"deepEqual\",\n\t\t\"notDeepEqual\",\n\t\t\"strictEqual\",\n\t\t\"notStrictEqual\",\n\t\t\"raises\",\n\t\t\"$\",\n\t\t\"fr\"\n\t]\n}"
  },
  {
    "path": "tests/log/logHandler_test.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <title>Test Loghandler</title>\n        <!-- Load local jQuery. This can be overridden with a ?jquery=___ param. -->\n        <script src=\"../../bower_components/jquery/dist/jquery.min.js\" type=\"text/javascript\"></script>\n        <script src=\"../../src/utils/jquery-class.js\" type=\"text/javascript\"></script>\n        <!-- Load local QUnit. -->\n        <link rel=\"stylesheet\" href=\"../../bower_components/qunit/qunit/qunit.css\" media=\"screen\">\n        <script src=\"../../bower_components/qunit/qunit/qunit.js\"></script>\n        <!-- Load local lib and tests. -->\n        <script src=\"../../src/log/log-handler.js\"></script>\n        <script src=\"logHandler_test.js\"></script>\n    </head>\n    <body>\n        <div id=\"qunit\"></div>\n        <div id=\"qunit-fixture\">\n            <span>lame test markup</span> <span>normal test markup</span> <span>awesome test markup</span>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "tests/log/logHandler_test.js",
    "content": "/*\n * ======== A Handy Little QUnit Reference ======== http://api.qunitjs.com/\n * \n * Test methods: module(name, {[setup][ ,teardown]}) test(name, callback) expect(numberOfAssertions) stop(increment) start(decrement) Test assertions: ok(value, [message]) equal(actual, expected,\n * [message]) notEqual(actual, expected, [message]) deepEqual(actual, expected, [message]) notDeepEqual(actual, expected, [message]) strictEqual(actual, expected, [message]) notStrictEqual(actual,\n * expected, [message]) throws(block, [expected], [message])\n */\n\ntest( 'logDisabled ',function ()\n{\n    var logHandler = new fr.ina.amalia.player.log.LogHandler();\n    equal( logHandler.trace( 'Class','Info Test' ),null );\n    equal( logHandler.info( 'info' ),'info' );\n} );\n\ntest( 'logEnabled',function ()\n{\n    var logHandler = new fr.ina.amalia.player.log.LogHandler( {\n        enabled : true\n    } );\n    equal( logHandler.trace( 'Class','trace' ),'Class:trace' );\n    equal( logHandler.info( 'info' ),'info' );\n} );\n"
  },
  {
    "path": "tests/player/LoaderBase_test.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <title>Test LoaderBase</title>\n        <!-- Load local jQuery. This can be overridden with a ?jquery=___ param. -->\n        <script src=\"../../bower_components/jquery/dist/jquery.min.js\" type=\"text/javascript\"></script>\n        <script src=\"../../src/utils/jquery-class.js\" type=\"text/javascript\"></script>\n        <!-- Load local QUnit. -->\n        <link rel=\"stylesheet\" href=\"../../bower_components/qunit/qunit/qunit.css\" media=\"screen\">\n        <script src=\"../../bower_components/qunit/qunit/qunit.js\"></script>\n        <script src=\"../../src/log/log-handler.js\"></script>\n        <!-- Load local lib and tests. -->\n        <script src=\"../../src/player/loaderBase.js\" type=\"text/javascript\"></script>\n        <script src=\"LoaderBase_test.js\"></script>\n    </head>\n    <body>\n        <div id=\"qunit\"></div>\n        <div id=\"qunit-fixture\">\n            <span>lame test markup</span>\n            <span>normal test markup</span>\n            <span>awesome test markup</span>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "tests/player/LoaderBase_test.js",
    "content": "/*\n ======== A Handy Little QUnit Reference ========\n http://api.qunitjs.com/\n \n Test methods:\n module(name, {[setup][ ,teardown]})\n test(name, callback)\n expect(numberOfAssertions)\n stop(increment)\n start(decrement)\n Test assertions:\n ok(value, [message])\n equal(actual, expected, [message])\n notEqual(actual, expected, [message])\n deepEqual(actual, expected, [message])\n notDeepEqual(actual, expected, [message])\n strictEqual(actual, expected, [message])\n notStrictEqual(actual, expected, [message])\n throws(block, [expected], [message])\n */\n\ntest( 'Test Load Video Entities',function () {\n    var settings = {\n        debug : true\n    };\n    var loadData = null;\n    var loader = new fr.ina.amalia.player.LoaderBase( settings );\n    loader.load( 'data/VideoEntities.json' );\n    deepEqual( loader.getSendData(),{} );\n\n    asyncTest( \"Entities TEST load data test: 3 second later!\",function () {\n        expect( 1 );\n        setTimeout( function () {\n            loadData = loader.getData();\n            equal( loadData.id,\"entities-2305843779355017216\" );\n            //equal(loadData.type, \"entities\");\n            start();\n        },3000 );\n    } );\n\n} );\n\ntest( 'Test Load Video Keyframes',function () {\n    var settings = {\n        debug : true\n    };\n    var loadData = null;\n    var loader = new fr.ina.amalia.player.LoaderBase( settings );\n    loader.load( 'data/VideoKeyframes.json' );\n    deepEqual( loader.getSendData(),{} );\n\n    asyncTest( \"Keyframes TEST load data test: 3 second later!\",function () {\n        expect( 1 );\n        setTimeout( function () {\n            loadData = loader.getData();\n            equal( loadData.id,\"keyframes-2305843779355017216\" );\n            //equal(loadData.type, \"keyframes\");\n            start();\n        },3000 );\n    } );\n\n} );\n\n"
  },
  {
    "path": "tests/player/PlayerErrorCode_test.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\">\n        <title>Test PlayerErrorCode</title>\n        <!-- Load local jQuery. This can be overridden with a ?jquery=___ param. -->\n        <script src=\"../../bower_components/jquery/dist/jquery.min.js\" type=\"text/javascript\"></script>\n        <script src=\"../../src/utils/jquery-class.js\" type=\"text/javascript\"></script>\n        <!-- Load local QUnit. -->\n        <link rel=\"stylesheet\" href=\"../../bower_components/qunit/qunit/qunit.css\" media=\"screen\"/>\n        <script src=\"../../bower_components/qunit/qunit/qunit.js\"></script>\n        <script src=\"../../src/log/log-handler.js\"></script>\n\n        <!-- Load local lib and tests. -->\n        <script src=\"../../src/player/constants/player-error-code.js\" type=\"text/javascript\"></script>\n        <script src=\"PlayerErrorCode_test.js\"></script>\n    </head>\n    <body>\n        <div id=\"qunit\"></div>\n        <div id=\"qunit-fixture\">\n            <span>lame test markup</span>\n            <span>normal test markup</span>\n            <span>awesome test markup</span>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "tests/player/PlayerErrorCode_test.js",
    "content": "/*\n ======== A Handy Little QUnit Reference ========\n http://api.qunitjs.com/\n \n Test methods:\n module(name, {[setup][ ,teardown]})\n test(name, callback)\n expect(numberOfAssertions)\n stop(increment)\n start(decrement)\n Test assertions:\n ok(value, [message])\n equal(actual, expected, [message])\n notEqual(actual, expected, [message])\n deepEqual(actual, expected, [message])\n notDeepEqual(actual, expected, [message])\n strictEqual(actual, expected, [message])\n notStrictEqual(actual, expected, [message])\n throws(block, [expected], [message])\n */\n\ntest( 'messageCode ',function () {\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.ACCESS_DENIED ),'Accès refusé.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.FILE_NOT_FOUND ),'Une erreur s\\'est produite sur votre lecteur.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.EXCEPTION ),'An exception occurred.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.HTTP_ERROR ),'Http response at 400 or 500 level.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.ABORT ),'Votre requête a été interrompue.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.TIMEOUT ),'Demande dépassé.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage( fr.ina.amalia.player.PlayerErrorCode.CUSTOM_ERROR ),'Une erreur s\\'est produite sur votre lecteur.' );\n    equal( fr.ina.amalia.player.PlayerErrorCode.getMessage(),'Une erreur s\\'est produite sur votre lecteur.' );\n} );\n\n\n"
  },
  {
    "path": "tests/player/data/VideoEntities.json",
    "content": "\n{\n    \"message\": \"Video 2305843779355017216 and dictionary 1 found\",\n    \"status\": 0,\n    \"data\": {\n        \"data\": [\n            {\n                \"class\": [\n                    {\n                        \"attribute\": [\n                            {\n                                \"value\": \"Notre-Dame\",\n                                \"name\": \"label\"\n                            }\n                        ],\n                        \"id\": \"2\",\n                        \"score\": 13.0\n                    }\n                ]\n            }\n        ],\n        \"localisation\": [\n            {\n                \"sublocalisations\": {\n                    \"localisation\": [\n                        {\n                            \"data\": {\n                                \"classref\": [\n                                    {\n                                        \"id\": \"2\",\n                                        \"score\": 13.0\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:26.0680\",\n                            \"tclevel\": 1\n                        }\n                    ]\n                },\n                \"type\": \"entity\",\n                \"tcin\": \"00:00:00.0000\",\n                \"tcout\": \"00:04:26.0680\",\n                \"tclevel\": 0\n            }\n        ],\n        \"id\": \"entities-2305843779355017216\",\n        \"type\": \"entities\",\n        \"algorithm\": \"SnoopDic\",\n        \"processor\": \"Ina Research\",\n        \"processed\": 1402384283835\n    }\n}"
  },
  {
    "path": "tests/player/data/VideoKeyframes.json",
    "content": "\n{\n    \"message\": \"Video 2305843779355017216 found\",\n    \"status\": 0,\n    \"data\": {\n        \"localisation\": [\n            {\n                \"sublocalisations\": {\n                    \"localisation\": [\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355017832.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16169\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:03.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355018680.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16178\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:07.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355019360.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16166\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:10.0720\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355020232.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16193\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:15.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355020832.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16223\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:18.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355021336.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16208\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:20.0600\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355021840.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16165\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:23.0120\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355022496.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16260\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:26.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355023152.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16255\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:29.0680\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355023992.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16204\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:33.0880\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355024880.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16240\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:38.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355025248.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16253\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:40.0160\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355025544.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16195\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:41.0640\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355026256.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16242\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:45.0200\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355027328.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16196\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:50.0560\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355027864.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16182\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:53.0240\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355028048.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16157\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:54.0160\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355028440.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16272\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:00:56.0120\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355029320.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16261\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:00.0520\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355030824.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16203\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:08.0040\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355031880.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16218\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:13.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355032776.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16257\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:17.0800\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355034000.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16215\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:23.0920\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355034928.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16224\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:28.0560\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355035576.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16213\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:31.0800\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355035992.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16168\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:33.0880\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355036336.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16241\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:35.0600\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355036680.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16263\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:37.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355037032.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16258\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:39.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355037320.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16179\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:40.0520\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355037600.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16191\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:41.0920\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355038040.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16250\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:44.0120\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355038472.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16252\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:46.0280\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355038880.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16154\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:48.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355039448.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16212\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:51.0160\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355039968.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16245\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:53.0760\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355040816.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16175\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:01:58.0000\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355041768.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16180\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:02.0760\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355042392.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16172\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:05.0880\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355042952.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16187\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:08.0680\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355043280.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16205\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:10.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355043512.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16243\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:11.0480\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355043864.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16265\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:13.0240\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355044496.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16226\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:16.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355045200.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16229\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:19.0920\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355045640.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16207\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:22.0120\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355046032.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16164\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:24.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355046376.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16233\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:25.0800\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355046696.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16274\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:27.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355047408.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16200\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:30.0960\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355048128.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16264\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:34.0560\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355048584.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16201\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:36.0840\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355049112.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16171\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:39.0480\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355049672.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16202\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:42.0280\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355050080.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16216\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:44.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355050352.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16210\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:45.0680\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355050856.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16176\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:48.0200\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355051416.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16160\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:51.0000\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355051848.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16225\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:53.0160\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355052216.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16159\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:55.0000\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355052496.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16206\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:56.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355052728.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16238\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:57.0560\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355053024.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16235\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:02:59.0040\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355053344.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16268\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:00.0640\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355053648.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16152\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:02.0160\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355053976.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16181\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:03.0800\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355054296.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16244\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:05.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355054992.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16249\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:08.0880\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355055712.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16194\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:12.0480\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355056120.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16170\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:14.0520\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355056584.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16251\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:16.0840\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355057024.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16192\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:19.0040\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355057456.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16222\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:21.0200\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355057832.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16248\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:23.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355058216.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16221\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:25.0000\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355058616.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16217\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:27.0000\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355059016.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16237\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:29.0000\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355059400.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16185\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:30.0920\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355059880.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16155\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:33.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355060344.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16197\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:35.0640\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355060728.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16230\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:37.0560\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355061120.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16236\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:39.0520\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355061648.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16220\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:42.0160\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355062328.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16158\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:45.0560\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355062872.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16228\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:48.0280\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355063304.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16211\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:50.0440\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355063688.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16270\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:52.0360\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355063992.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16198\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:53.0880\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355064296.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16214\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:55.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355064640.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16239\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:57.0120\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355065056.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16232\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:03:59.0200\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355065360.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16219\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:00.0720\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355065664.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16262\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:02.0240\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355066304.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16227\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:05.0440\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355066904.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16184\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:08.0440\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355067320.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16269\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:10.0520\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355067768.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16256\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:12.0760\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355068096.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16189\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:14.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355068376.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16174\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:15.0800\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355068672.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16156\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:17.0280\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355068968.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16151\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:18.0760\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355069336.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16273\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:20.0600\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355069696.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16234\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:22.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355070064.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16254\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:24.0240\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355070552.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16266\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:26.0680\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355071168.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16267\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:29.0760\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355071704.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16259\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:32.0440\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355072232.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16247\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:35.0080\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355072960.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16162\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:38.0720\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355073752.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16173\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:42.0680\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355074312.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16209\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:45.0480\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355074584.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16177\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:46.0840\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355074984.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16153\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:48.0840\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355075560.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16188\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:51.0720\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355076064.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16246\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:54.0240\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355076496.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16186\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:56.0400\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355076976.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16231\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:04:58.0800\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355077384.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16163\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:00.0840\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355078480.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16183\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:06.0320\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355079592.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16167\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:11.0880\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355079856.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16271\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:13.0200\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355080440.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16161\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:16.0120\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355082456.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16199\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:26.0200\",\n                            \"tclevel\": 1\n                        },\n                        {\n                            \"data\": {\n                                \"class\": [\n                                    {\n                                        \"attribute\": [\n                                            {\n                                                \"value\": \"kf/test/2305843779355017216/keyframes/2305843779355084496.jpg\",\n                                                \"name\": \"uri\"\n                                            },\n                                            {\n                                                \"value\": \"cut\",\n                                                \"name\": \"type\"\n                                            }\n                                        ],\n                                        \"id\": \"16190\",\n                                        \"score\": 1\n                                    }\n                                ]\n                            },\n                            \"tc\": \"00:05:36.0400\",\n                            \"tclevel\": 1\n                        }\n                    ]\n                },\n                \"type\": \"keyframe\",\n                \"tcin\": \"00:00:00.0000\",\n                \"tcout\": \"00:05:36.0400\",\n                \"tclevel\": 0\n            }\n        ],\n        \"id\": \"keyframes-2305843779355017216\",\n        \"type\": \"keyframes\",\n        \"algorithm\": \"SnoopKF\",\n        \"processor\": \"Ina Research\",\n        \"processed\": 1402384304522\n    }\n}"
  }
]