diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..84ea879
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,10 @@
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+[{*.js, *.html, *.json}]
+indent_style = space
+indent_size = 2
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..414c19c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+
+chrome/app/player
+firefox/data/player
+safari.safariextension/app/player
diff --git a/chrome/app/middleware.js b/chrome/app/middleware.js
new file mode 100644
index 0000000..aa21323
--- /dev/null
+++ b/chrome/app/middleware.js
@@ -0,0 +1,11 @@
+window.addEventListener('load', function() {
+ chrome.runtime.onMessage.addListener(
+ function(request, sender, sendResponse) {
+ if (request.selectedText === undefined) return;
+
+ window.plugin = (window.plugin || new VLibras.Plugin());
+ window.plugin.translate(request.selectedText);
+ });
+
+ chrome.runtime.sendMessage({ready: true});
+});
diff --git a/chrome/background.js b/chrome/background.js
new file mode 100644
index 0000000..f970737
--- /dev/null
+++ b/chrome/background.js
@@ -0,0 +1,51 @@
+var popup = undefined;
+var selectedText = undefined;
+
+// Creates the context menu to translate texts
+chrome.contextMenus.create({
+ id: 'translate_contextmenu',
+ title: 'Traduzir \'%s\' para LIBRAS',
+ contexts: ['selection']
+}, function () {
+ if ( chrome.runtime.lastError ) console.log(chrome.runtime.lastError.message);
+});
+
+// Listening the event click
+chrome.contextMenus.onClicked.addListener( function (info) {
+ selectedText = info.selectionText;
+
+ // Creates the window if it exists
+ if ( popup === undefined ) {
+ chrome.windows.create({
+ url: "app/player/index.html",
+ top: 10,
+ left: 10,
+ width: 540,
+ height: 450,
+ type: "popup"
+ }, function (w) {
+ popup = w;
+ });
+ } else {
+ chrome.windows.update(popup.id, {focused: true}, function () {
+ chrome.runtime.sendMessage({selectedText: selectedText});
+ selectedText = undefined;
+ });
+ }
+});
+
+// Frees variable if the popup doesn't to exist anymore
+chrome.windows.onRemoved.addListener( function (windowId) {
+ if (windowId == popup.id) {
+ popup = undefined;
+ }
+});
+
+// Listening the ready event of the popup
+chrome.runtime.onMessage.addListener(
+ function(request, sender, sendResponse) {
+ if (request.ready === true && selectedText !== undefined) {
+ chrome.runtime.sendMessage({selectedText: selectedText});
+ selectedText = undefined;
+ }
+ });
diff --git a/chrome/icons/vlibras128.png b/chrome/icons/vlibras128.png
new file mode 100644
index 0000000..c4e8bee
Binary files /dev/null and b/chrome/icons/vlibras128.png differ
diff --git a/chrome/icons/vlibras16.png b/chrome/icons/vlibras16.png
new file mode 100644
index 0000000..6092655
Binary files /dev/null and b/chrome/icons/vlibras16.png differ
diff --git a/chrome/icons/vlibras48.png b/chrome/icons/vlibras48.png
new file mode 100644
index 0000000..f6c7194
Binary files /dev/null and b/chrome/icons/vlibras48.png differ
diff --git a/chrome/manifest.json b/chrome/manifest.json
new file mode 100644
index 0000000..a45a0e9
--- /dev/null
+++ b/chrome/manifest.json
@@ -0,0 +1,20 @@
+{
+ "manifest_version": 2,
+ "name": "VLibras",
+ "description": "Um tradutor de Português para LIBRAS.",
+ "version": "2.0.0",
+ "background": {
+ "scripts": ["background.js"],
+ "persistent": false
+ },
+ "permissions": [
+ "contextMenus",
+ "http://vlibras.lavid.ufpb.br/"
+ ],
+ "icons": {
+ "16": "icons/vlibras16.png",
+ "48": "icons/vlibras48.png",
+ "128": "icons/vlibras128.png"
+ },
+ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
+}
diff --git a/firefox/README.md b/firefox/README.md
new file mode 100644
index 0000000..aa2ae01
--- /dev/null
+++ b/firefox/README.md
@@ -0,0 +1,2 @@
+#VLibras Plugin
+Um tradutor de Português para LIBRAS
\ No newline at end of file
diff --git a/firefox/data/delegator.js b/firefox/data/delegator.js
new file mode 100644
index 0000000..9ae4851
--- /dev/null
+++ b/firefox/data/delegator.js
@@ -0,0 +1,7 @@
+'use strict';
+
+self.port.on('selectedText', function(selectedText) {
+ var event = new CustomEvent('plugin:selectedText', { 'detail': selectedText });
+
+ document.dispatchEvent(event);
+});
diff --git a/firefox/data/middleware.js b/firefox/data/middleware.js
new file mode 100644
index 0000000..1a5bd7e
--- /dev/null
+++ b/firefox/data/middleware.js
@@ -0,0 +1,6 @@
+document.addEventListener('plugin:selectedText', function(e) {
+ if (e.detail === undefined) return;
+
+ window.plugin = (window.plugin || new VLibras.Plugin());
+ window.plugin.translate(e.detail);
+});
diff --git a/firefox/index.js b/firefox/index.js
new file mode 100644
index 0000000..babfcae
--- /dev/null
+++ b/firefox/index.js
@@ -0,0 +1,62 @@
+'use strict';
+
+// Imports
+let self = require('sdk/self');
+let { Ci } = require('chrome');
+
+let cm = require("sdk/context-menu");
+let pm = require('sdk/page-mod');
+
+let browser = require('sdk/window/utils').getMostRecentBrowserWindow();
+
+//Globals
+let app = {
+ window: null,
+ worker: null,
+ selectedText: ""
+};
+
+// Context menu
+cm.Item({
+ label: "Traduzir para LIBRAS",
+ context: cm.SelectionContext(),
+ contentScript: 'self.on("context", function () {' +
+ ' var text = window.getSelection().toString();' +
+ ' if (text.length > 20)' +
+ ' text = text.substr(0, 20) + "...";' +
+ ' return "Traduzir \'" + text + "\' para LIBRAS";' +
+ '});' +
+ 'self.on("click", function (node, data) {' +
+ ' self.postMessage( window.getSelection().toString() );' +
+ '});',
+ onMessage: function(selectedText) {
+ app.selectedText = selectedText;
+
+ if ( !(app.window instanceof Ci.nsIDOMWindow) ) {
+ app.window = browser.open(
+ self.data.url('player/index.html'),
+ 'VLibras Plugin',
+ 'width=540,height=450,menubar=no,resizable=yes'
+ );
+ } else {
+ app.worker.port.emit('selectedText', app.selectedText);
+ app.window.focus();
+ }
+ }
+});
+
+// Page mod
+pm.PageMod({
+ include: self.data.url('player/index.html'),
+ contentScriptWhen: 'end',
+ contentScriptFile: self.data.url('delegator.js'),
+ onAttach: function(worker) {
+ app.worker = worker;
+
+ app.worker.port.emit('selectedText', app.selectedText);
+
+ app.worker.on('detach', function () {
+ app.worker = undefined;
+ });
+ }
+});
diff --git a/firefox/package.json b/firefox/package.json
new file mode 100644
index 0000000..8ee18fe
--- /dev/null
+++ b/firefox/package.json
@@ -0,0 +1,11 @@
+{
+ "title": "VLibras Plugin",
+ "name": "vlibras-plugin",
+ "version": "2.0.0",
+ "description": "Um tradutor de Português para LIBRAS",
+ "main": "index.js",
+ "author": "LAViD",
+ "engines": {
+ "firefox": ">=38.0a1"
+ }
+}
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..0e372a0
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,40 @@
+var gulp = require('gulp');
+var webpack = require('webpack-stream');
+
+var webpackConfig = require('./webpack.config.js');
+
+var options = {
+ dest: {
+ chrome: 'chrome/app/player',
+ firefox: 'firefox/data/player',
+ safari: 'safari.safariextension/app/player'
+ }
+};
+
+function build(target) {
+ var destPath = options.dest[target];
+
+ gulp.src('node_modules/vlibras/src/target/**/*')
+ .pipe(gulp.dest(destPath + '/target'));
+
+ gulp.src('plugin/index.js')
+ .pipe(webpack(webpackConfig))
+ .pipe(gulp.dest(destPath));
+
+ gulp.src(['index.html', 'assets/*'], {cwd: 'plugin', base: 'plugin'})
+ .pipe(gulp.dest(destPath));
+}
+
+gulp.task('build:chrome', function () {
+ build('chrome');
+});
+
+gulp.task('build:firefox', function () {
+ build('firefox');
+});
+
+gulp.task('build:safari', function () {
+ build('safari');
+});
+
+gulp.task('build', ['build:chrome', 'build:firefox', 'build:safari']);
diff --git a/keys/vlibrasplugin-chrome.pem b/keys/vlibrasplugin-chrome.pem
new file mode 100644
index 0000000..f287079
--- /dev/null
+++ b/keys/vlibrasplugin-chrome.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8UI+z9pRhF8Kv
+WC3Y2eI41b4AJeROjrO3rVeBG+FYjMuOB0r1byFUlebGP/Nep7nZ9eEUZ4E71lkp
+WDyYLfZTb+gum6MQAeh8Wd7aBh9qSa4ru08ieu/8HxmHdfUFpWph3yPMV0TdAXVh
+sbMLWcvzu+AmEkPM09KtXENyAOkR63ncll7HtmGFdxicTzNWuIhrvrhsd1xtuS1c
+g4vk1XEJ5mcSnVLVGojAI8uwY4QEAGC2bUMzmD6mtZxySHu5XxNnk1xFdTxebd1K
+4u0HyDgpY7epRsnkYHQq9HZ7Smet2JW4tHmd/5nPMZXFztZ/bUSuOqAmXvmLXvmw
+JSpVnEENAgMBAAECggEAVCmFMB8iDYq6/fbg9qvaSSBt/E7zDJEGjS6xjwhQ3GyA
+hQeJXsu+D7m3HB90u3cmvtz/LUldHssbqji/TBwunPfEwx/X7s2LhBf+W16lJ7Gq
+X+0k3vomy31ywXHcght9wiQiMa9HHacLORBcPtRxLItpEFrVZnO9ErHLiuYRRGo+
+EKP79o1dVZ5kldJ7toB4ok+KIDu2PJJShj4nXj7mKNlGZPH2hYn+NG7ZLFOjUp09
+AKjTIE5WADt5bOKN9Wo2HiCG63zLZsaJmeYh2v3TxnpAj+W/TMxV/y0kIe7gzqw1
+2qxdBmLBILNM8AkwadpbJRuC6aynd41sqejix3uemQKBgQDzONGl0Yf8fg5EyX3r
+w/OldzZv/Comy6Ka/aq8I88clFBlEoD2os+lF6IDAgMj102awjiI+fxOe81C7Pza
+a5eGHeq3qaE0E7v7ggcW7YXlFs9YC6RikKEkGCFnnaxeBf+/iXICcf32GzZkSow2
+JTedIJIbu/oca2UFpVsK4inkHwKBgQDGNUZMmJ1ca4WdpmP6mUCwqEgOZ+UcG/3K
+4Sy0BtJOB1wVoQOzM1q2FUurCqps81cghUbtJyeRk4y4eI1rOEbz8m0UB0hWyEq1
+pJJiz0nei7ficAIHO5e2elDV2lJ7KNxYeb3YN2NXbGmwmsI/IoudPp+wghnl4Xns
+2JcBFZdVUwKBgQCBDcb/6xvyt5gum5M7BDBFvemepfhfuXFAOBTd20pL6c52ssjH
+FUCEYvPOEMVFwTomQaPp2msr4bjaKBFKfAhcW4dtiI5Gqan3G9lTKxj+o4nFN/gz
+bHZ0RnGDH0CUay52nSFndsDAU1QbBws6t6i4sM2V9boSVlG2GzUhozuDFQKBgG2P
+kTZRvdoSTR9hlC43fDyqKHpeD5TEHDuC0HUKuEENbGdAQ41Qu7med98p3B6xO93s
+DdKVLBgaGVT2viUgTIgX3xeNFv1xZWqWd5xKvKhZTuHJzDU3SZtCbWBod+NAKNx3
+HutfYng2ow1N3kWL0OSwcxDcOXiKdBzpdD7TMn2lAoGAH3xcQ9pI1aCeNxrnyvsJ
+1EFlSX+3HQvjd9JOVemiVd6jP5SbrM6DplzimYPVmJhlAcq+wi8/dHyOGMRCb7ZV
+Yv44hSheny/RhfMcePwubLBhVULRrdg2LWOEnSgrLIWNe2CCJ+xzgr2mEM+vGazI
+zVWbI5s+shUi96/ZSZMjxGo=
+-----END PRIVATE KEY-----
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3def9a2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "vlibras-plugin",
+ "version": "0.0.1",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "nouislider": "^8.3.0",
+ "vlibras": "~0.0.1"
+ },
+ "devDependencies": {
+ "css-loader": "^0.23.1",
+ "es6-promise": "^3.1.2",
+ "gulp": "^3.9.1",
+ "node-sass": "^3.4.2",
+ "raw-loader": "^0.5.1",
+ "sass-loader": "^3.1.2",
+ "style-loader": "^0.13.0",
+ "webpack": "^1.12.14",
+ "webpack-stream": "^3.1.0"
+ }
+}
diff --git a/plugin/Plugin.js b/plugin/Plugin.js
new file mode 100644
index 0000000..80895c9
--- /dev/null
+++ b/plugin/Plugin.js
@@ -0,0 +1,72 @@
+var VLibras = require('vlibras');
+
+var InfoScreen = require('components/InfoScreen');
+var InfoScreenBtn = require('components/InfoScreenBtn');
+var Controls = require('components/Controls');
+var Progress = require('components/Progress');
+var MessageBox = require('components/MessageBox');
+
+function Plugin() {
+ this.player = new VLibras.Player({
+ progress: Progress
+ });
+
+ this.element = document.querySelector('[vp]');
+ this.controls = new Controls(this.player);
+ this.info = new InfoScreen();
+ this.infoBtn = new InfoScreenBtn(this.info);
+ this.messageBox = new MessageBox();
+ this.loadingRef = null;
+
+ this.messageBox.load(this.element.querySelector('[vp-message-box]'));
+ this.player.load(this.element);
+
+ this.player.on('load', function () {
+ // Loading components
+ this.controls.load(this.element.querySelector('[vp-controls]'));
+ this.info.load(this.element.querySelector('[vp-info-screen]'));
+ this.infoBtn.load(this.element.querySelector('[vp-info-screen-btn]'));
+ }.bind(this));
+
+ this.info.on('show', function () {
+ this.player.pause();
+ }.bind(this));
+
+ this.player.on('translate:start', function () {
+ this.loadingRef = this.messageBox.show('info', 'Traduzindo...');
+ }.bind(this));
+
+ this.player.on('translate:end', function () {
+ this.messageBox.hide(this.loadingRef);
+ }.bind(this));
+
+ this.player.on('error', function (err) {
+ switch(err) {
+ case 'compatibility_error':
+ this.messageBox.show(
+ 'warning',
+ 'O seu computador não suporta o WebGL. Por favor, atualize os drivers de vídeo.'
+ );
+ break;
+ case 'translation_error':
+ this.messageBox.show(
+ 'warning',
+ 'Não foi possivel traduzir. Por favor, verifique sua internet.',
+ 3000
+ );
+ break;
+ case 'internal_error':
+ this.messageBox.show(
+ 'warning',
+ 'Ops! Ocorreu um problema, por favor entre em contato com a gente.'
+ );
+ break;
+ }
+ }.bind(this));
+};
+
+Plugin.prototype.translate = function (text) {
+ this.player.translate(text);
+};
+
+module.exports = Plugin;
diff --git a/plugin/assets/About.png b/plugin/assets/About.png
new file mode 100644
index 0000000..42a02c2
Binary files /dev/null and b/plugin/assets/About.png differ
diff --git a/plugin/assets/Base.png b/plugin/assets/Base.png
new file mode 100644
index 0000000..f64ed96
Binary files /dev/null and b/plugin/assets/Base.png differ
diff --git a/plugin/assets/CamaraDosDps.png b/plugin/assets/CamaraDosDps.png
new file mode 100644
index 0000000..8a2479f
Binary files /dev/null and b/plugin/assets/CamaraDosDps.png differ
diff --git a/plugin/assets/Close.png b/plugin/assets/Close.png
new file mode 100644
index 0000000..d349896
Binary files /dev/null and b/plugin/assets/Close.png differ
diff --git a/plugin/assets/CounterOff.png b/plugin/assets/CounterOff.png
new file mode 100644
index 0000000..4610392
Binary files /dev/null and b/plugin/assets/CounterOff.png differ
diff --git a/plugin/assets/CounterOn.png b/plugin/assets/CounterOn.png
new file mode 100644
index 0000000..fcb3701
Binary files /dev/null and b/plugin/assets/CounterOn.png differ
diff --git a/plugin/assets/Fast.png b/plugin/assets/Fast.png
new file mode 100644
index 0000000..d17922a
Binary files /dev/null and b/plugin/assets/Fast.png differ
diff --git a/plugin/assets/Gov.png b/plugin/assets/Gov.png
new file mode 100644
index 0000000..90f49a1
Binary files /dev/null and b/plugin/assets/Gov.png differ
diff --git a/plugin/assets/LSD.png b/plugin/assets/LSD.png
new file mode 100644
index 0000000..2efd1b1
Binary files /dev/null and b/plugin/assets/LSD.png differ
diff --git a/plugin/assets/Lavid.png b/plugin/assets/Lavid.png
new file mode 100644
index 0000000..ca2673b
Binary files /dev/null and b/plugin/assets/Lavid.png differ
diff --git a/plugin/assets/Pause.png b/plugin/assets/Pause.png
new file mode 100644
index 0000000..62025bd
Binary files /dev/null and b/plugin/assets/Pause.png differ
diff --git a/plugin/assets/Play.png b/plugin/assets/Play.png
new file mode 100644
index 0000000..7653e7a
Binary files /dev/null and b/plugin/assets/Play.png differ
diff --git a/plugin/assets/RNP.png b/plugin/assets/RNP.png
new file mode 100644
index 0000000..6afef55
Binary files /dev/null and b/plugin/assets/RNP.png differ
diff --git a/plugin/assets/Repeat.png b/plugin/assets/Repeat.png
new file mode 100644
index 0000000..2f0fd64
Binary files /dev/null and b/plugin/assets/Repeat.png differ
diff --git a/plugin/assets/Slow.png b/plugin/assets/Slow.png
new file mode 100644
index 0000000..4324fe1
Binary files /dev/null and b/plugin/assets/Slow.png differ
diff --git a/plugin/assets/Stop.png b/plugin/assets/Stop.png
new file mode 100644
index 0000000..967fd18
Binary files /dev/null and b/plugin/assets/Stop.png differ
diff --git a/plugin/assets/Subtitles.png b/plugin/assets/Subtitles.png
new file mode 100644
index 0000000..661157b
Binary files /dev/null and b/plugin/assets/Subtitles.png differ
diff --git a/plugin/assets/SubtitlesDisabled.png b/plugin/assets/SubtitlesDisabled.png
new file mode 100644
index 0000000..911b646
Binary files /dev/null and b/plugin/assets/SubtitlesDisabled.png differ
diff --git a/plugin/assets/ToLeft.png b/plugin/assets/ToLeft.png
new file mode 100644
index 0000000..42c7284
Binary files /dev/null and b/plugin/assets/ToLeft.png differ
diff --git a/plugin/assets/ToRight.png b/plugin/assets/ToRight.png
new file mode 100644
index 0000000..1326465
Binary files /dev/null and b/plugin/assets/ToRight.png differ
diff --git a/plugin/assets/UF.png b/plugin/assets/UF.png
new file mode 100644
index 0000000..52875e3
Binary files /dev/null and b/plugin/assets/UF.png differ
diff --git a/plugin/assets/UFCG.png b/plugin/assets/UFCG.png
new file mode 100644
index 0000000..29ca9a6
Binary files /dev/null and b/plugin/assets/UFCG.png differ
diff --git a/plugin/assets/favicon.png b/plugin/assets/favicon.png
new file mode 100644
index 0000000..f6c7194
Binary files /dev/null and b/plugin/assets/favicon.png differ
diff --git a/plugin/assets/logo.png b/plugin/assets/logo.png
new file mode 100644
index 0000000..6d46d07
Binary files /dev/null and b/plugin/assets/logo.png differ
diff --git a/plugin/assets/progresslogo.png b/plugin/assets/progresslogo.png
new file mode 100644
index 0000000..a568710
Binary files /dev/null and b/plugin/assets/progresslogo.png differ
diff --git a/plugin/components/Controls/controls.html b/plugin/components/Controls/controls.html
new file mode 100644
index 0000000..1a75f93
--- /dev/null
+++ b/plugin/components/Controls/controls.html
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/plugin/components/Controls/controls.scss b/plugin/components/Controls/controls.scss
new file mode 100644
index 0000000..67ebafe
--- /dev/null
+++ b/plugin/components/Controls/controls.scss
@@ -0,0 +1,57 @@
+@import '~scss/flexbox';
+
+.controls {
+ @include flexbox;
+ @include align-items(center);
+
+ padding: 10px;
+ width: calc(100% - 10px);
+ height: 60px;
+ margin: 0 5px;
+
+ background: url(assets/Base.png) no-repeat center center;
+ background-size: 100% 100%;
+
+ .controls-play, .controls-slider, .controls-subtitles {
+ margin: 0 10px;
+ }
+
+ .controls-play:before { content: url(assets/Play.png); }
+ &.playing .controls-play:before { content: url(assets/Pause.png); }
+ &.stopped .controls-play:before { content: url(assets/Repeat.png); }
+ .controls-stop:before { content: url(assets/Stop.png); }
+ &.subtitles .controls-subtitles:before { content: url(assets/Subtitles.png) }
+ .controls-subtitles:before { content: url(assets/SubtitlesDisabled.png) }
+
+ .controls-slider {
+ @include flexbox;
+ @include align-items(center);
+
+ -webkit-appearance: none;
+ width: 100%;
+ margin: 2.5px 0;
+ height: 7px;
+ background-color: transparent;
+
+ &:before { content: url(assets/Slow.png); }
+ &:after { padding-left: 5px; content: url(assets/Fast.png); }
+
+ .slider {
+ width: 100%;
+ height: 7px;
+
+ &.noUi-target { box-shadow: none; border: 0; }
+ &.noUi-connect { background-color: #fff; }
+ .noUi-background { background-color: #333; box-shadow: none; }
+ .noUi-handle {
+ width: 20px;
+ height: 20px;
+ left: -8px;
+ top: -8px;
+ border-radius: 50%;
+ }
+
+ .noUi-handle:after, .noUi-handle:before { display: none; }
+ }
+ }
+}
diff --git a/plugin/components/Controls/index.js b/plugin/components/Controls/index.js
new file mode 100644
index 0000000..33536bc
--- /dev/null
+++ b/plugin/components/Controls/index.js
@@ -0,0 +1,74 @@
+var noUiSlider = require('nouislider');
+require('nouislider/distribute/nouislider.min.css');
+
+var controlsTpl = require('./controls.html');
+require('./controls.scss');
+
+function Controls(player) {
+ this.player = player;
+
+ this.player.on('animation:play', function () {
+ console.log('animation:play');
+ this.element.classList.remove('stopped');
+ this.element.classList.add('playing');
+ }.bind(this));
+
+ this.player.on('animation:pause', function () {
+ console.log('animation:pause');
+ this.element.classList.remove('playing');
+ this.element.classList.remove('stopped');
+ }.bind(this));
+
+ this.player.on('animation:end', function () {
+ console.log('animation:end');
+ this.element.classList.remove('playing');
+ this.element.classList.add('stopped');
+ }.bind(this));
+}
+
+Controls.prototype.load = function (element) {
+ this.element = element;
+ this.element.innerHTML = controlsTpl;
+ this.element.classList.add('controls');
+ this.element.classList.add('subtitles');
+
+ var play = this.element.querySelector('.controls-play');
+ var stop = this.element.querySelector('.controls-stop');
+ var slider = this.element.querySelector('.controls-slider .slider');
+ var subtitles = this.element.querySelector('.controls-subtitles');
+
+ play.addEventListener('click', function () {
+ if (this.element.classList.contains('playing')) {
+ this.player.pause();
+ } else if(this.element.classList.contains('stopped')) {
+ this.player.repeat();
+ } else {
+ this.player.continue();
+ }
+ }.bind(this));
+
+ stop.addEventListener('click', function () {
+ this.player.stop();
+ }.bind(this));
+
+ noUiSlider.create(slider, {
+ start: 1.1,
+ step: 0.05,
+ connect: 'lower',
+ range: {
+ min: 0,
+ max: 2
+ }
+ });
+
+ slider.noUiSlider.on('update', function (value) {
+ this.player.setSpeed(Number(value[0]));
+ }.bind(this));
+
+ subtitles.addEventListener('click', function () {
+ this.element.classList.toggle('subtitles');
+ this.player.toggleSubtitle();
+ }.bind(this));
+};
+
+module.exports = Controls;
diff --git a/plugin/components/InfoScreen/index.js b/plugin/components/InfoScreen/index.js
new file mode 100644
index 0000000..84bc999
--- /dev/null
+++ b/plugin/components/InfoScreen/index.js
@@ -0,0 +1,66 @@
+var inherits = require('inherits');
+var EventEmitter = require('events').EventEmitter;
+
+var infoScreenTpl = require('./info-screen.html');
+require('./info-screen.scss');
+
+function InfoScreen() {
+ this.visible = false;
+}
+
+inherits(InfoScreen, EventEmitter);
+
+InfoScreen.prototype.load = function (element) {
+ this.element = element;
+ this.element.innerHTML = infoScreenTpl;
+ this.element.classList.add('info-screen');
+
+ var main = this.element.querySelector('#info-main');
+ var realizadores = this.element.querySelector('#info-realizadores');
+ var left = this.element.querySelector('.arrow-left');
+ var right = this.element.querySelector('.arrow-right');
+ var bullets = this.element.querySelectorAll('.info-bullet');
+
+ left.addEventListener('click', function() {
+ realizadores.classList.remove('active');
+ main.classList.add('active');
+
+ this.classList.remove('active');
+ right.classList.add('active');
+
+ bullets[1].classList.remove('active');
+ bullets[0].classList.add('active');
+ });
+
+ right.addEventListener('click', function() {
+ main.classList.remove('active');
+ realizadores.classList.add('active');
+
+ this.classList.remove('active');
+ left.classList.add('active');
+
+ bullets[0].classList.remove('active');
+ bullets[1].classList.add('active');
+ });
+
+ this.hide();
+};
+
+InfoScreen.prototype.toggle = function () {
+ if (this.visible) this.hide();
+ else this.show();
+};
+
+InfoScreen.prototype.hide = function () {
+ this.visible = false;
+ this.element.classList.remove('active');
+ this.emit('hide');
+};
+
+InfoScreen.prototype.show = function () {
+ this.visible = true;
+ this.element.classList.add('active');
+ this.emit('show');
+};
+
+module.exports = InfoScreen;
diff --git a/plugin/components/InfoScreen/info-screen.html b/plugin/components/InfoScreen/info-screen.html
new file mode 100644
index 0000000..f57b05d
--- /dev/null
+++ b/plugin/components/InfoScreen/info-screen.html
@@ -0,0 +1,35 @@
+
+

+
+
+
+
+

+
+
+
+
O VLibras é uma ferramenta aberta de distribuição livre, desenvolvida para melhorar o acesso à informação das pessoas surdas brasileiras.
+
Qualquer dúvida ou questionamento, envie uma mensagem para o Núcleo de Pesquisa e Extensão LAViD - Centro de Informática - UFPB através do e-mail. contato@lavid.ufpb.br
+
+
+
+
Realizadores
+

+

+

+

+

+

+

+
+
+
+
+
+
+
+

+
diff --git a/plugin/components/InfoScreen/info-screen.scss b/plugin/components/InfoScreen/info-screen.scss
new file mode 100644
index 0000000..3453893
--- /dev/null
+++ b/plugin/components/InfoScreen/info-screen.scss
@@ -0,0 +1,116 @@
+.info-screen {
+ position: absolute;
+ left: 0;
+ top: 0;
+
+ padding: 1em;
+
+ width: 100%;
+ height: 100%;
+
+ background-color: white;
+ color: black;
+ font-family: Arial, sans-serif;
+ text-align: center;
+
+ align-items: center;
+ -webkit-align-items: center;
+ font-size: 16px;
+
+ display: none;
+
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ &.active {
+ display: -webkit-flex;
+ display: flex;
+ }
+
+ .arrow {
+ flex-grow: 1;
+ -webkit-flex-grow: 1;
+ visibility: hidden;
+ }
+
+ .arrow.active {
+ visibility: visible;
+ }
+
+ #info-tabset {
+ flex-grow: 4;
+ display: -webkit-flex;
+ display: flex;
+ flex-direction: column;
+ -webkit-flex-direction: column;
+ padding: 0 1em;
+ }
+
+ #info-main {
+ flex-direction: column;
+ -webkit-flex-direction: column;
+ }
+
+ #info-realizadores {
+ flex-direction: row;
+ -webkit-flex-direction: row;
+ flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ justify-content: center;
+ -webkit-justify-content: center;
+ }
+
+ #info-realizadores .logo {
+ margin-left: 2em;
+ width: 20%;
+ min-width: 60px;
+ max-width: 80px;
+ }
+
+ #gov-logo.logo {
+ width: 100%;
+ margin-left: 0;
+ min-width: 200px;
+ max-width: 500px;
+ }
+
+ #ufpb-logo {
+ width: 10%;
+ }
+
+ .info-tab {
+ flex-grow: 3;
+ -webkit-flex-grow: 3;
+ display: none;
+ }
+
+ .info-tab.active {
+ display: block;
+ }
+
+ #info-meta p {
+ margin: .5em 0 0;
+ }
+
+ #info-meta a {
+ text-decoration: none;
+ }
+
+ .info-bullet:before {
+ content: url(assets/CounterOff.png);
+ }
+
+ .info-bullet.active:before {
+ content: url(assets/CounterOn.png);
+ }
+
+ #info-tab-bullets {
+ width: 100%;
+ height: 16px;
+ text-align: center;
+ flex-grow: 1;
+ -webkit-flex-grow: 1;
+ margin-top: 10%;
+ }
+}
diff --git a/plugin/components/InfoScreenBtn/index.js b/plugin/components/InfoScreenBtn/index.js
new file mode 100644
index 0000000..370a13c
--- /dev/null
+++ b/plugin/components/InfoScreenBtn/index.js
@@ -0,0 +1,17 @@
+require('./info-screen-btn.scss');
+
+function InfoScreenBtn(screen) {
+ this.screen = screen;
+}
+
+InfoScreenBtn.prototype.load = function (element) {
+ this.element = element;
+ this.element.classList.add('info-screen-btn');
+
+ this.element.addEventListener('click', function () {
+ this.element.classList.toggle('active');
+ this.screen.toggle();
+ }.bind(this));
+};
+
+module.exports = InfoScreenBtn;
diff --git a/plugin/components/InfoScreenBtn/info-screen-btn.scss b/plugin/components/InfoScreenBtn/info-screen-btn.scss
new file mode 100644
index 0000000..4987da1
--- /dev/null
+++ b/plugin/components/InfoScreenBtn/info-screen-btn.scss
@@ -0,0 +1,7 @@
+.info-screen-btn:before {
+ content: url(assets/About.png);
+}
+
+.info-screen-btn.active:before {
+ content: url(assets/Close.png);
+}
diff --git a/plugin/components/MessageBox/index.js b/plugin/components/MessageBox/index.js
new file mode 100644
index 0000000..919d888
--- /dev/null
+++ b/plugin/components/MessageBox/index.js
@@ -0,0 +1,56 @@
+require('./message-box.scss');
+
+var messageBoxTlp = '';
+
+function MessageBox() {
+ this.element = null;
+ this.message = null;
+}
+
+MessageBox.LEVELS = ['info', 'warning', 'success', 'default'];
+
+MessageBox.prototype.load = function (element) {
+ this.element = element;
+ this.element.classList.add('message-box');
+ this.element.innerHTML = messageBoxTlp;
+
+ this.hide();
+};
+
+MessageBox.prototype.hide = function(message) {
+ if (message !== this.message) return;
+
+ this.message = null;
+ this.element.classList.remove('active');
+
+ MessageBox.LEVELS.forEach(function(level) {
+ this.element.classList.remove(level);
+ }, this);
+};
+
+MessageBox.prototype.show = function(level, message, time) {
+ var self = this;
+
+ level = MessageBox.LEVELS.indexOf(level) == -1 ? 'info' : level;
+
+ this.hide();
+
+ self.element.classList.add('active');
+ self.element.classList.add(level);
+ self.element.querySelector('.message').innerHTML = message;
+
+ self.message = {
+ text: message
+ };
+
+ var ref = self.message;
+ if (time) {
+ setTimeout(function () {
+ self.hide(ref);
+ }, time + 1);
+ }
+
+ return this.message;
+};
+
+module.exports = MessageBox;
diff --git a/plugin/components/MessageBox/message-box.scss b/plugin/components/MessageBox/message-box.scss
new file mode 100644
index 0000000..aba548e
--- /dev/null
+++ b/plugin/components/MessageBox/message-box.scss
@@ -0,0 +1,46 @@
+.message-box {
+ position: fixed;
+ top: -5em;
+ left: 0;
+ width: 100%;
+ padding: 1em;
+ font-size: 1em;
+ word-wrap: break-word;
+ color: #000;
+ opacity: 0;
+ -moz-transition: all .15s ease .15s;
+ -webkit-transition: all .15s ease .15s;
+ transition: all .15s ease .15s;
+ -moz-box-shadow: 0px 2px 5px #888888;
+ -webkit-box-shadow: 0px 2px 5px #888888;
+ box-shadow: 0px 2px 5px #888888;
+
+ &.active {
+ top: 0;
+ opacity: 1;
+ }
+
+ &.info {
+ background-color: #3b8bba;
+ color: #ffffff;
+ }
+
+ &.warning {
+ background-color: #f8ecad;
+ color: #7c6d1f;
+ }
+
+ &.success {
+ background-color: #d6e9c6;
+ color: #468847;
+ }
+
+ &.default {
+ background-color: #e6e6e6;
+ color: #8c8c8c;
+ }
+
+ a {
+ color: inherit;
+ }
+}
diff --git a/plugin/components/Progress/index.js b/plugin/components/Progress/index.js
new file mode 100644
index 0000000..2e3d24f
--- /dev/null
+++ b/plugin/components/Progress/index.js
@@ -0,0 +1,40 @@
+require('./progress.scss');
+var progressTpl = require('./progress.html');
+
+function Progress(wrapper) {
+ this.progress = 0.0;
+ this.message = '';
+
+ this.element = document.createElement('div');
+ this.element.classList.add('progress');
+ this.element.innerHTML = progressTpl;
+
+ wrapper.appendChild(this.element);
+
+ this.Update();
+}
+
+Progress.prototype.SetProgress = function (progress) {
+ if (this.progress < progress) {
+ this.progress = progress;
+ }
+
+ this.Update();
+};
+
+Progress.prototype.SetMessage = function (message) {
+ this.message = message;
+ this.Update();
+};
+
+Progress.prototype.Clear = function() {
+ var parent = this.element.parentNode;
+ parent.removeChild(this.element);
+};
+
+Progress.prototype.Update = function() {
+ var progress = this.element.querySelector('.progressbar > .bar');
+ progress.style.width = (this.progress * 100) + '%';
+};
+
+module.exports = Progress;
diff --git a/plugin/components/Progress/progress.html b/plugin/components/Progress/progress.html
new file mode 100644
index 0000000..78728c0
--- /dev/null
+++ b/plugin/components/Progress/progress.html
@@ -0,0 +1,4 @@
+
+
diff --git a/plugin/components/Progress/progress.scss b/plugin/components/Progress/progress.scss
new file mode 100644
index 0000000..add1b33
--- /dev/null
+++ b/plugin/components/Progress/progress.scss
@@ -0,0 +1,33 @@
+.progress {
+ $loading-brand-height: 150px;
+ $loading-progress-height: 15px;
+ $loading-width: 150px;
+ $loading-height: $loading-brand-height + $loading-progress-height;
+
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-left: -$loading-width / 2;
+ margin-top: -($loading-height / 2 + 10);
+ width: $loading-width;
+ text-align: center;
+
+ .brand {
+ height: $loading-brand-height;
+ }
+
+ .progressbar {
+ margin: 10px;
+ background-color: #444444;
+ width: 100%;
+ height: $loading-progress-height;
+ padding: 0;
+ margin: 0;
+
+ .bar {
+ width: 0;
+ background-color: #003366;
+ height: $loading-progress-height;
+ }
+ }
+}
diff --git a/plugin/index.html b/plugin/index.html
new file mode 100644
index 0000000..e31a70a
--- /dev/null
+++ b/plugin/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+ VLibras Plugin
+
+
+
+
+
+
+
+
diff --git a/plugin/index.js b/plugin/index.js
new file mode 100644
index 0000000..c76bd0b
--- /dev/null
+++ b/plugin/index.js
@@ -0,0 +1,6 @@
+var Plugin = require('./Plugin.js');
+var window = require('window');
+
+require('scss/vlibras-plugin.scss');
+
+window.VLibras.Plugin = Plugin;
diff --git a/plugin/scss/_flexbox.scss b/plugin/scss/_flexbox.scss
new file mode 100644
index 0000000..039811c
--- /dev/null
+++ b/plugin/scss/_flexbox.scss
@@ -0,0 +1,394 @@
+// Flexbox Mixins
+// http://philipwalton.github.io/solved-by-flexbox/
+// https://github.com/philipwalton/solved-by-flexbox
+//
+// Copyright (c) 2013 Brian Franco
+//
+// Permission 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:
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+// THE 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.
+//
+// This is a set of mixins for those who want to mess around with flexbox
+// using the native support of current browsers. For full support table
+// check: http://caniuse.com/flexbox
+//
+// Basically this will use:
+//
+// * Fallback, old syntax (IE10, mobile webkit browsers - no wrapping)
+// * Final standards syntax (FF, Safari, Chrome, IE11, Opera)
+//
+// This was inspired by:
+//
+// * http://dev.opera.com/articles/view/advanced-cross-browser-flexbox/
+//
+// With help from:
+//
+// * http://w3.org/tr/css3-flexbox/
+// * http://the-echoplex.net/flexyboxes/
+// * http://msdn.microsoft.com/en-us/library/ie/hh772069(v=vs.85).aspx
+// * http://css-tricks.com/using-flexbox/
+// * http://dev.opera.com/articles/view/advanced-cross-browser-flexbox/
+// * https://developer.mozilla.org/en-us/docs/web/guide/css/flexible_boxes
+
+//----------------------------------------------------------------------
+
+// Flexbox Containers
+//
+// The 'flex' value causes an element to generate a block-level flex
+// container box.
+//
+// The 'inline-flex' value causes an element to generate a inline-level
+// flex container box.
+//
+// display: flex | inline-flex
+//
+// http://w3.org/tr/css3-flexbox/#flex-containers
+//
+// (Placeholder selectors for each type, for those who rather @extend)
+
+@mixin flexbox {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -moz-flex;
+ display: -ms-flexbox;
+ display: flex;
+}
+
+%flexbox { @include flexbox; }
+
+//----------------------------------
+
+@mixin inline-flex {
+ display: -webkit-inline-box;
+ display: -webkit-inline-flex;
+ display: -moz-inline-flex;
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+}
+
+%inline-flex { @include inline-flex; }
+
+//----------------------------------------------------------------------
+
+// Flexbox Direction
+//
+// The 'flex-direction' property specifies how flex items are placed in
+// the flex container, by setting the direction of the flex container's
+// main axis. This determines the direction that flex items are laid out in.
+//
+// Values: row | row-reverse | column | column-reverse
+// Default: row
+//
+// http://w3.org/tr/css3-flexbox/#flex-direction-property
+
+@mixin flex-direction($value: row) {
+ @if $value == row-reverse {
+ -webkit-box-direction: reverse;
+ -webkit-box-orient: horizontal;
+ } @else if $value == column {
+ -webkit-box-direction: normal;
+ -webkit-box-orient: vertical;
+ } @else if $value == column-reverse {
+ -webkit-box-direction: reverse;
+ -webkit-box-orient: vertical;
+ } @else {
+ -webkit-box-direction: normal;
+ -webkit-box-orient: horizontal;
+ }
+ -webkit-flex-direction: $value;
+ -moz-flex-direction: $value;
+ -ms-flex-direction: $value;
+ flex-direction: $value;
+}
+// Shorter version:
+@mixin flex-dir($args...) { @include flex-direction($args...); }
+
+//----------------------------------------------------------------------
+
+// Flexbox Wrap
+//
+// The 'flex-wrap' property controls whether the flex container is single-line
+// or multi-line, and the direction of the cross-axis, which determines
+// the direction new lines are stacked in.
+//
+// Values: nowrap | wrap | wrap-reverse
+// Default: nowrap
+//
+// http://w3.org/tr/css3-flexbox/#flex-wrap-property
+
+@mixin flex-wrap($value: nowrap) {
+ // No Webkit Box fallback.
+ -webkit-flex-wrap: $value;
+ -moz-flex-wrap: $value;
+ @if $value == nowrap {
+ -ms-flex-wrap: none;
+ } @else {
+ -ms-flex-wrap: $value;
+ }
+ flex-wrap: $value;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Flow (shorthand)
+//
+// The 'flex-flow' property is a shorthand for setting the 'flex-direction'
+// and 'flex-wrap' properties, which together define the flex container's
+// main and cross axes.
+//
+// Values: |
+// Default: row nowrap
+//
+// http://w3.org/tr/css3-flexbox/#flex-flow-property
+
+@mixin flex-flow($values: (row nowrap)) {
+ // No Webkit Box fallback.
+ -webkit-flex-flow: $values;
+ -moz-flex-flow: $values;
+ -ms-flex-flow: $values;
+ flex-flow: $values;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Order
+//
+// The 'order' property controls the order in which flex items appear within
+// their flex container, by assigning them to ordinal groups.
+//
+// Default: 0
+//
+// http://w3.org/tr/css3-flexbox/#order-property
+
+@mixin order($int: 0) {
+ -webkit-box-ordinal-group: $int + 1;
+ -webkit-order: $int;
+ -moz-order: $int;
+ -ms-flex-order: $int;
+ order: $int;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Grow
+//
+// The 'flex-grow' property sets the flex grow factor. Negative numbers
+// are invalid.
+//
+// Default: 0
+//
+// http://w3.org/tr/css3-flexbox/#flex-grow-property
+
+@mixin flex-grow($int: 0) {
+ -webkit-box-flex: $int;
+ -webkit-flex-grow: $int;
+ -moz-flex-grow: $int;
+ -ms-flex-positive: $int;
+ flex-grow: $int;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Shrink
+//
+// The 'flex-shrink' property sets the flex shrink factor. Negative numbers
+// are invalid.
+//
+// Default: 1
+//
+// http://w3.org/tr/css3-flexbox/#flex-shrink-property
+
+@mixin flex-shrink($int: 1) {
+ -webkit-flex-shrink: $int;
+ -moz-flex-shrink: $int;
+ -ms-flex-negative: $int;
+ flex-shrink: $int;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Basis
+//
+// The 'flex-basis' property sets the flex basis. Negative lengths are invalid.
+//
+// Values: Like "width"
+// Default: auto
+//
+// http://www.w3.org/TR/css3-flexbox/#flex-basis-property
+
+@mixin flex-basis($value: auto) {
+ -webkit-flex-basis: $value;
+ -moz-flex-basis: $value;
+ -ms-flex-preferred-size: $value;
+ flex-basis: $value;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox "Flex" (shorthand)
+//
+// The 'flex' property specifies the components of a flexible length: the
+// flex grow factor and flex shrink factor, and the flex basis. When an
+// element is a flex item, 'flex' is consulted instead of the main size
+// property to determine the main size of the element. If an element is
+// not a flex item, 'flex' has no effect.
+//
+// Values: none | ||
+// Default: See individual properties (1 1 0).
+//
+// http://w3.org/tr/css3-flexbox/#flex-property
+
+@mixin flex($fg: 1, $fs: null, $fb: null) {
+
+ // Set a variable to be used by box-flex properties
+ $fg-boxflex: $fg;
+
+ // Box-Flex only supports a flex-grow value so let's grab the
+ // first item in the list and just return that.
+ @if type-of($fg) == 'list' {
+ $fg-boxflex: nth($fg, 1);
+ }
+
+ -webkit-box-flex: $fg-boxflex;
+ -webkit-flex: $fg $fs $fb;
+ -moz-box-flex: $fg-boxflex;
+ -moz-flex: $fg $fs $fb;
+ -ms-flex: $fg $fs $fb;
+ flex: $fg $fs $fb;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Justify Content
+//
+// The 'justify-content' property aligns flex items along the main axis
+// of the current line of the flex container. This is done after any flexible
+// lengths and any auto margins have been resolved. Typically it helps distribute
+// extra free space leftover when either all the flex items on a line are
+// inflexible, or are flexible but have reached their maximum size. It also
+// exerts some control over the alignment of items when they overflow the line.
+//
+// Note: 'space-*' values not supported in older syntaxes.
+//
+// Values: flex-start | flex-end | center | space-between | space-around
+// Default: flex-start
+//
+// http://w3.org/tr/css3-flexbox/#justify-content-property
+
+@mixin justify-content($value: flex-start) {
+ @if $value == flex-start {
+ -webkit-box-pack: start;
+ -ms-flex-pack: start;
+ } @else if $value == flex-end {
+ -webkit-box-pack: end;
+ -ms-flex-pack: end;
+ } @else if $value == space-between {
+ -webkit-box-pack: justify;
+ -ms-flex-pack: justify;
+ } @else if $value == space-around {
+ -ms-flex-pack: distribute;
+ } @else {
+ -webkit-box-pack: $value;
+ -ms-flex-pack: $value;
+ }
+ -webkit-justify-content: $value;
+ -moz-justify-content: $value;
+ justify-content: $value;
+}
+// Shorter version:
+@mixin flex-just($args...) { @include justify-content($args...); }
+
+//----------------------------------------------------------------------
+
+// Flexbox Align Items
+//
+// Flex items can be aligned in the cross axis of the current line of the
+// flex container, similar to 'justify-content' but in the perpendicular
+// direction. 'align-items' sets the default alignment for all of the flex
+// container's items, including anonymous flex items. 'align-self' allows
+// this default alignment to be overridden for individual flex items. (For
+// anonymous flex items, 'align-self' always matches the value of 'align-items'
+// on their associated flex container.)
+//
+// Values: flex-start | flex-end | center | baseline | stretch
+// Default: stretch
+//
+// http://w3.org/tr/css3-flexbox/#align-items-property
+
+@mixin align-items($value: stretch) {
+ @if $value == flex-start {
+ -webkit-box-align: start;
+ -ms-flex-align: start;
+ } @else if $value == flex-end {
+ -webkit-box-align: end;
+ -ms-flex-align: end;
+ } @else {
+ -webkit-box-align: $value;
+ -ms-flex-align: $value;
+ }
+ -webkit-align-items: $value;
+ -moz-align-items: $value;
+ align-items: $value;
+}
+
+//----------------------------------
+
+// Flexbox Align Self
+//
+// Values: auto | flex-start | flex-end | center | baseline | stretch
+// Default: auto
+
+@mixin align-self($value: auto) {
+ // No Webkit Box Fallback.
+ -webkit-align-self: $value;
+ -moz-align-self: $value;
+ @if $value == flex-start {
+ -ms-flex-item-align: start;
+ } @else if $value == flex-end {
+ -ms-flex-item-align: end;
+ } @else {
+ -ms-flex-item-align: $value;
+ }
+ align-self: $value;
+}
+
+//----------------------------------------------------------------------
+
+// Flexbox Align Content
+//
+// The 'align-content' property aligns a flex container's lines within the
+// flex container when there is extra space in the cross-axis, similar to
+// how 'justify-content' aligns individual items within the main-axis. Note,
+// this property has no effect when the flexbox has only a single line.
+//
+// Values: flex-start | flex-end | center | space-between | space-around | stretch
+// Default: stretch
+//
+// http://w3.org/tr/css3-flexbox/#align-content-property
+
+@mixin align-content($value: stretch) {
+ // No Webkit Box Fallback.
+ -webkit-align-content: $value;
+ -moz-align-content: $value;
+ @if $value == flex-start {
+ -ms-flex-line-pack: start;
+ } @else if $value == flex-end {
+ -ms-flex-line-pack: end;
+ } @else {
+ -ms-flex-line-pack: $value;
+ }
+ align-content: $value;
+}
diff --git a/plugin/scss/vlibras-plugin.scss b/plugin/scss/vlibras-plugin.scss
new file mode 100644
index 0000000..3bb5280
--- /dev/null
+++ b/plugin/scss/vlibras-plugin.scss
@@ -0,0 +1,59 @@
+html {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+*, *:before, *:after {
+ -webkit-box-sizing: inherit;
+ -moz-box-sizing: inherit;
+ box-sizing: inherit;
+}
+
+html, body {
+ width: 100%;
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ overflow: hidden;
+ font-size: 16px;
+}
+
+[vp] {
+ height: 100%;
+ width: 100%;
+ z-index: 1;
+
+ canvas { width: 100%; height: 100% }
+
+ [vp-info-screen-btn] {
+ position: fixed;
+ top: 7px;
+ right: 5px;
+ z-index: 4;
+ }
+
+ [vp-message-box] {
+ z-index: 5;
+ }
+
+ [vp-info-screen] {
+ z-index: 3;
+ }
+
+ [vp-controls] {
+ position: fixed;
+ bottom: 7px;
+ max-width: 560px;
+ z-index: 2;
+ }
+}
+
+@media (min-width: 560px) {
+ [vp] [vp-controls] {
+ left: 50%;
+ margin-left: -275px;
+ }
+}
diff --git a/safari.safariextension/Info.plist b/safari.safariextension/Info.plist
new file mode 100644
index 0000000..b1f1784
--- /dev/null
+++ b/safari.safariextension/Info.plist
@@ -0,0 +1,53 @@
+
+
+
+
+ Author
+ LAViD
+ Builder Version
+ 10600.6.3
+ CFBundleDisplayName
+ VLibras Plugin
+ CFBundleIdentifier
+ br.ufpb.lavid.vlibras
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleShortVersionString
+ 0.2.0
+ CFBundleVersion
+ 2
+ Chrome
+
+ Database Quota
+ 52428800
+ Global Page
+ global.html
+
+ Content
+
+ Scripts
+
+ Start
+
+ scripts/contextmenu.js
+
+
+
+ Description
+ Um tradutor de portugues para LIBRAS
+ DeveloperIdentifier
+ 3PTX2S8KDQ
+ ExtensionInfoDictionaryVersion
+ 1.0
+ Permissions
+
+ Website Access
+
+ Include Secure Pages
+
+ Level
+ All
+
+
+
+
diff --git a/safari.safariextension/app/middleware.js b/safari.safariextension/app/middleware.js
new file mode 100644
index 0000000..e98fbf9
--- /dev/null
+++ b/safari.safariextension/app/middleware.js
@@ -0,0 +1,12 @@
+window.resizeTo(540, 470);
+
+document.addEventListener('player:verified', function() {
+ safari.self.addEventListener('message', function (request) {
+ if (request.name !== 'plugin:selectedText' && request.message === undefined) return;
+
+ window.plugin = (window.plugin || new VLibras.Plugin());
+ window.plugin.translate(request.message);
+ });
+
+ safari.self.tab.dispatchMessage('page:ready', true);
+});
diff --git a/safari.safariextension/background.js b/safari.safariextension/background.js
new file mode 100644
index 0000000..fc3e8e1
--- /dev/null
+++ b/safari.safariextension/background.js
@@ -0,0 +1,49 @@
+var popup = null;
+var selectedText = undefined;
+var appURL = safari.extension.baseURI + 'app/player/index.html';
+
+safari.application.addEventListener('contextmenu', function (event){
+ var selectedText = event.userInfo;
+
+ if (!selectedText) return;
+
+ if (selectedText.length > 20) {
+ selectedText = selectedText.substr(0, 20) + '...';
+ }
+
+ if (selectedText !== '') {
+ event.contextMenu.appendContextMenuItem('translateLibras', 'Traduzir ' + selectedText + ' para LIBRAS');
+ }
+}, false);
+
+safari.application.addEventListener('command', function (event){
+ if (event.command === 'translateLibras') {
+ selectedText = event.userInfo;
+
+ if (popup === null) {
+ popup = safari.application.openBrowserWindow().activeTab;
+ popup.url = appURL;
+
+ popup.addEventListener('close', function () {
+ popup = null;
+ });
+
+ popup.addEventListener('navigate', function (event) {
+ if (event.target.url !== appURL) {
+ popup = null;
+ }
+ });
+
+ popup.addEventListener('message', function (request) {
+ if (selectedText !== undefined && request.name === 'page:ready' && request.message == true) {
+ popup.page.dispatchMessage('plugin:selectedText', selectedText);
+ selectedText = undefined;
+ };
+ });
+ } else {
+ popup.browserWindow.activate();
+ popup.page.dispatchMessage('plugin:selectedText', selectedText);
+ selectedText = undefined;
+ }
+ }
+}, false);
diff --git a/safari.safariextension/global.html b/safari.safariextension/global.html
new file mode 100644
index 0000000..2d91fdb
--- /dev/null
+++ b/safari.safariextension/global.html
@@ -0,0 +1,8 @@
+
+
+ VLibras Plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/safari.safariextension/scripts/contextmenu.js b/safari.safariextension/scripts/contextmenu.js
new file mode 100644
index 0000000..ef99c7e
--- /dev/null
+++ b/safari.safariextension/scripts/contextmenu.js
@@ -0,0 +1,3 @@
+document.addEventListener('contextmenu', function (event){
+ safari.self.tab.setContextMenuEventUserInfo(event, window.getSelection().toString());
+}, false);
\ No newline at end of file
diff --git a/safari.safariextension/vlibras48.png b/safari.safariextension/vlibras48.png
new file mode 100644
index 0000000..f6c7194
Binary files /dev/null and b/safari.safariextension/vlibras48.png differ
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000..d9faa4a
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,21 @@
+var path = require('path');
+
+require('es6-promise').polyfill();
+
+module.exports = {
+ output: {
+ filename: 'vlibras-plugin.js'
+ },
+ resolve: {
+ root: path.join(__dirname, 'plugin')
+ },
+ externals: {
+ 'window': 'window'
+ },
+ module: {
+ loaders: [
+ { test: /\.s?css/, loaders: ['style', 'css?-url', 'sass'] },
+ { test: /\.html/, loaders: ['raw'] }
+ ]
+ }
+}
--
libgit2 0.21.2