├── .gitmodules ├── src ├── webext │ └── dummy.txt ├── react-native │ └── dummy.txt └── electron │ ├── icon.ico │ ├── icon.png │ ├── nativeshot.exe │ ├── package.json │ ├── index.html │ ├── log.txt │ ├── mac-nix-child-main.cc │ ├── win-child-main.cpp │ └── main.js ├── .babelrc ├── AMO-REVIEWER-README.txt ├── .gitignore ├── package.json ├── README.md └── gulpfile.js /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/webext/dummy.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/react-native/dummy.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/electron/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noitidart/NativeShot/HEAD/src/electron/icon.ico -------------------------------------------------------------------------------- /src/electron/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noitidart/NativeShot/HEAD/src/electron/icon.png -------------------------------------------------------------------------------- /src/electron/nativeshot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noitidart/NativeShot/HEAD/src/electron/nativeshot.exe -------------------------------------------------------------------------------- /src/electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NativeShot", 3 | "version": "0.1.0", 4 | "main": "main.js", 5 | "devDependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "es2017"], 3 | "plugins": ["transform-object-rest-spread"], 4 | "ignore": [ 5 | "3rd/**/*", 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/electron/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello World! 6 | 7 | 8 |

Hello World!

9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/electron/log.txt: -------------------------------------------------------------------------------- 1 | startup 2 | reading length 3 | length: 6 4 | reading string 5 | read string: ["ping"] 6 | payload_str: "ping" 7 | reading length 8 | startup 9 | reading length 10 | length: 6 11 | reading string 12 | read string: ["ping"] 13 | payload_str: "ping" 14 | reading length 15 | length: 6 16 | reading string 17 | read string: ["ping"] 18 | payload_str: "ping" 19 | reading length 20 | -------------------------------------------------------------------------------- /AMO-REVIEWER-README.txt: -------------------------------------------------------------------------------- 1 | BUILD INSTRUCTIONS 2 | cd into .tidy.ignore 3 | npm install 4 | gulp 5 | it will create dist.xpi and ./dist 6 | dist.xpi should match perfectly to the upload xpi 7 | 8 | 3RD PARTY SOURCES 9 | * ./resources/scripts/3rd/ocrad.js - https://github.com/antimatter15/ocrad.js/blob/5b0af624ebfd70cf45ddf55c58eaf25718133ba3/ocrad.js 10 | * ./resources/scripts/3rd/gocr.js - https://github.com/antimatter15/gocr.js/tree/d820e0651cf819e9649a837d83125724a2c1cc37 11 | * ./resources/scripts/3rd/tesseract.js - https://cdn.rawgit.com/naptha/tesseract.js/master/lib/worker.2015.07.26.js 12 | * ./resources/scripts/3rd/react-redux.js - https://npmcdn.com/react-redux@latest/dist/react-redux.min.js 13 | * ./resources/scripts/3rd/redux.js - https://npmcdn.com/redux@3.5.2/dist/redux.min.js 14 | * ./resources/scripts/3rd/react-with-addons.js - https://fb.me/react-with-addons-15.1.0.min.js 15 | * ./resources/scripts/3rd/react-dom.js - https://fb.me/react-dom-15.1.0.min.js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | supplement/ 4 | .* 5 | !.babelrc 6 | !.gitignore 7 | *.xpi 8 | !-unsigned.xpi 9 | 10 | #### below is auto generated .gitignore content by Github app 11 | # Windows image file caches 12 | Thumbs.db 13 | ehthumbs.db 14 | 15 | # Folder config file 16 | Desktop.ini 17 | 18 | # Recycle Bin used on file shares 19 | $RECYCLE.BIN/ 20 | 21 | # Windows Installer files 22 | *.cab 23 | *.msi 24 | *.msm 25 | *.msp 26 | 27 | # Windows shortcuts 28 | *.lnk 29 | 30 | # ========================= 31 | # Operating System Files 32 | # ========================= 33 | 34 | # OSX 35 | # ========================= 36 | 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear in the root of a volume 45 | .DocumentRevisions-V100 46 | .fseventsd 47 | .Spotlight-V100 48 | .TemporaryItems 49 | .Trashes 50 | .VolumeIcon.icns 51 | 52 | # Directories potentially created on remote AFP share 53 | .AppleDB 54 | .AppleDesktop 55 | Network Trash Folder 56 | Temporary Items 57 | .apdisk 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "build-babel", 3 | "version": "1.0.0", 4 | "description": "All my scripts are now babelified", 5 | "main": "index.js", 6 | "dependencies": { 7 | "gulp-rename": "^1.2.2", 8 | "nan": "^2.5.0", 9 | "react": "^15.3.2", 10 | "react-dom": "^15.3.2", 11 | "react-redux": "^4.4.5", 12 | "react-router": "^3.0.0", 13 | "redux": "^3.6.0" 14 | }, 15 | "devDependencies": { 16 | "babel-plugin-transform-object-rest-spread": "^6.16.0", 17 | "babel-polyfill": "^6.16.0", 18 | "babel-preset-es2015": "^6.16.0", 19 | "babel-preset-es2017": "^6.16.0", 20 | "fs": "0.0.1-security", 21 | "gulp": "^3.9.1", 22 | "gulp-babel": "^6.1.2", 23 | "gulp-clean": "^0.3.2", 24 | "gulp-contains": "^1.1.0", 25 | "gulp-if": "^2.0.1", 26 | "gulp-insert": "^0.5.0", 27 | "gulp-js-obfuscator": "^1.0.0", 28 | "gulp-jscrambler": "^1.0.2", 29 | "gulp-jshint": "^2.0.1", 30 | "gulp-replace": "^0.5.4", 31 | "gulp-src-ordered-globs": "^1.0.3", 32 | "gulp-util": "^3.0.7", 33 | "gulp-zip": "^3.2.0", 34 | "jshint": "^2.9.4" 35 | }, 36 | "scripts": { 37 | "test": "echo \"Error: no test specified\" && exit 1" 38 | }, 39 | "author": "Noitidart ", 40 | "license": "ISC" 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Download / Install 2 | * Firefox - [AMO :: NativeShot](https://addons.mozilla.org/en-US/firefox/addon/nativeshot/) 3 | 4 | ## About 5 | NativeShot is not your typical screenshot addon. The typical screenshot addon takes an image of the HTML content in your current or other tab. NativeShot features a system wide hotkey (and also a toolbar button) which takes screenshot as you would by pressing the "Print Screen" or "Screenshot" key on your computer. Everything on every monitor is captured and then opened up for editing. After you select an action from the menu, such as "Upload to Imgur", the link to the screenshot is copied to your clipboard, and a notification is shown. 6 | 7 | ## Credits 8 | * [Dakirby309](http://dakirby309.deviantart.com/) for Icon [(source)](https://www.iconfinder.com/icons/99958/screenshot_icon) 9 | * @wadie for Desktop (Electron) and Mobile (React Native) Collaboration 10 | * Translation 11 | * Arabic - @noureddin [(BZ)](http://beta.babelzilla.org/accounts/profile/noureddin/) 12 | * Bulgarian - @AdmiralAnimE [(BZ)](http://beta.babelzilla.org/accounts/profile/AdmiralAnimE/) 13 | * Catalan - @TheRabbitter [(BZ)](http://beta.babelzilla.org/accounts/profile/Rabbitter/) 14 | * German - [AlexS.](http://beta.babelzilla.org/accounts/profile/AlexS./), [Aryx](http://beta.babelzilla.org/accounts/profile/Aryx/), @Endor8 [(BZ)](http://beta.babelzilla.org/accounts/profile/endor8/) 15 | * Spanish (Spain) - @AlejandroPerezMartin [(BZ)](http://beta.babelzilla.org/accounts/profile/AlePerez92/) 16 | * Estonian - @mdr-ksk [(BZ)](http://beta.babelzilla.org/accounts/profile/mdr.ksk/) 17 | * French - @tojazmin [(BZ)](http://beta.babelzilla.org/accounts/profile/tojazmin/) 18 | * Hungarian - [Gabesz](http://beta.babelzilla.org/accounts/profile/Gabesz/) 19 | * Italian - @AlessandroMenti [(BZ)](http://beta.babelzilla.org/accounts/profile/elgaton/) 20 | * Japanese - @marsf [(BZ)](http://beta.babelzilla.org/accounts/profile/mar/) 21 | * Lithuanian - @gymka [(BZ)](http://beta.babelzilla.org/accounts/profile/gymka/), @zygimantus [(BZ)](http://beta.babelzilla.org/accounts/profile/zygimantus/) 22 | * Dutch - @MarkH [(BZ)](http://beta.babelzilla.org/accounts/profile/markh/), @TonnesM [(BZ)](http://beta.babelzilla.org/accounts/profile/Tonnes/) 23 | * Polish - @teo951 [(BZ)](http://beta.babelzilla.org/accounts/profile/teo/) 24 | * Portuguese (Brazil) - @MarceloGhelman [(BZ)](http://beta.babelzilla.org/accounts/profile/ghelman/) 25 | * Portuguese (Portugal) - @Ricardo-Simoes [(BZ)](http://beta.babelzilla.org/accounts/profile/ricardosimoes/) 26 | * Romanian - @Jobava [(BZ)](http://beta.babelzilla.org/accounts/profile/jobaval10n/) 27 | * Russian - @insolor [(BZ)](http://beta.babelzilla.org/accounts/profile/insolor/), @veadarkin [(BZ)](http://beta.babelzilla.org/accounts/profile/veadarkin/) 28 | * Turkish - [alfapegasi](http://beta.babelzilla.org/accounts/profile/alfapegasi/) 29 | * Chinese (Simplified) - @yfdyh000 [(BZ)](http://beta.babelzilla.org/accounts/profile/yfdyh000/) 30 | * Chinese (Traditional) - @goldie-lin [(BZ)](http://beta.babelzilla.org/accounts/profile/goldie/) 31 | -------------------------------------------------------------------------------- /src/electron/mac-nix-child-main.cc: -------------------------------------------------------------------------------- 1 | // g++ main.cc -o nativeshot -std=c++11 2 | #include 3 | #include 4 | 5 | // start - debug 6 | #include 7 | #include 8 | #include 9 | #include // debug time 10 | 11 | template bool 12 | debug_log_rec(std::ostream& out, HeadType&& head) { 13 | out << head; 14 | out << std::endl; 15 | return true; 16 | } 17 | 18 | template bool 19 | debug_log_rec(std::ostream& out, HeadType&& head, TailTypes&&... tails) { 20 | out << head; 21 | out << " "; 22 | debug_log_rec(out, std::forward(tails)...); 23 | return true; 24 | } 25 | 26 | template bool 27 | debug_log(ArgTypes&&... args) { 28 | // return true; // prod 29 | std::fstream fs; 30 | fs.open("log.txt", std::fstream::app); 31 | debug_log_rec(fs, std::forward(args)...); 32 | fs.close(); 33 | return true; 34 | } 35 | 36 | int nowms() { 37 | using namespace std::chrono; 38 | milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); 39 | return ms.count(); 40 | } 41 | // end - debug 42 | 43 | bool read_u32(uint32_t* data) { 44 | return std::fread(reinterpret_cast(data), sizeof(uint32_t), 1, stdin) == 1; 45 | } 46 | 47 | bool read_string(std::string &str, uint32_t length) { 48 | str.resize(length); 49 | return std::fread(&str[0], sizeof(char), str.length(), stdin) == length; 50 | } 51 | 52 | bool write_u32(uint32_t data) { 53 | return std::fwrite(reinterpret_cast(&data), sizeof(uint32_t), 1, stdout) == 1; 54 | } 55 | 56 | bool write_string(const std::string &str) { 57 | return std::fwrite(&str[0], sizeof(char), str.length(), stdout) == str.length(); 58 | } 59 | 60 | bool get_message(std::string& str) { 61 | uint32_t length; 62 | debug_log("reading length"); 63 | while (true) { 64 | if (!read_u32(&length)) { 65 | // debug_log("failed to read length", "SHOULD I RETRY?"); 66 | // return false; // comment this if you want retry 67 | continue; // uncomment this if you want retry 68 | } 69 | break; 70 | } 71 | debug_log("length:", length); 72 | debug_log("reading string"); 73 | while (true) { 74 | if (!read_string(str, length)) { 75 | // debug_log("failed to read string", "SHOULD I RETRY?"); 76 | // return false; // comment this if you want retry 77 | continue; // uncomment this if you want retry 78 | } 79 | break; 80 | } 81 | debug_log("read string: [" + str + "]"); 82 | // debug_log(str.length()); 83 | return true; 84 | } 85 | 86 | bool send_message(const std::string& str) { 87 | //debug_log("writing length"); 88 | while (!write_u32(str.length())) { 89 | debug_log("failed to write length, for str:", str, "WILL RETRY"); 90 | } 91 | //debug_log("writing string"); 92 | while (!write_string(str)) { 93 | debug_log("failed to write string, for str:", str, "WILL RETRY"); 94 | } 95 | //debug_log("flushing"); 96 | while (std::fflush(stdout) != 0) { 97 | debug_log("failed to flush, for str:", str, "WILL RETRY"); 98 | } 99 | return true; 100 | } 101 | 102 | int main(void) { 103 | debug_log("startup"); 104 | 105 | std::string payload_str; 106 | while (get_message(payload_str)) { 107 | debug_log("payload_str:", payload_str); 108 | if (payload_str == "\"ping\"") { 109 | send_message("\"pong\""); 110 | } 111 | } 112 | 113 | 114 | debug_log("ending"); 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /src/electron/win-child-main.cpp: -------------------------------------------------------------------------------- 1 | // standard defines for win app - https://msdn.microsoft.com/en-us/library/bb384843.aspx 2 | #define WIN32 3 | #define UNICODE 4 | #define _UNICODE 5 | #define _WINDOWS 6 | 7 | // standard includes for win app - https://msdn.microsoft.com/en-us/library/bb384843.aspx 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // start - debug 14 | #include 15 | #include 16 | #include 17 | #include // debug time 18 | 19 | template bool 20 | debug_log_rec(std::ostream& out, HeadType&& head) { 21 | out << head; 22 | out << std::endl; 23 | return true; 24 | } 25 | 26 | template bool 27 | debug_log_rec(std::ostream& out, HeadType&& head, TailTypes&&... tails) { 28 | out << head; 29 | out << " "; 30 | debug_log_rec(out, std::forward(tails)...); 31 | return true; 32 | } 33 | 34 | template bool 35 | debug_log(ArgTypes&&... args) { 36 | // return true; // prod 37 | std::fstream fs; 38 | fs.open("log.txt", std::fstream::app); 39 | debug_log_rec(fs, std::forward(args)...); 40 | fs.close(); 41 | return true; 42 | } 43 | 44 | int nowms() { 45 | using namespace std::chrono; 46 | milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); 47 | return ms.count(); 48 | } 49 | // end - debug 50 | 51 | bool read_u32(uint32_t* data) { 52 | return std::fread(reinterpret_cast(data), sizeof(uint32_t), 1, stdin) == 1; 53 | } 54 | 55 | bool read_string(std::string &str, uint32_t length) { 56 | str.resize(length); 57 | return std::fread(&str[0], sizeof(char), str.length(), stdin) == length; 58 | } 59 | 60 | bool write_u32(uint32_t data) { 61 | return std::fwrite(reinterpret_cast(&data), sizeof(uint32_t), 1, stdout) == 1; 62 | } 63 | 64 | bool write_string(const std::string &str) { 65 | return std::fwrite(&str[0], sizeof(char), str.length(), stdout) == str.length(); 66 | } 67 | 68 | bool get_message(std::string& str) { 69 | uint32_t length; 70 | debug_log("reading length"); 71 | while (true) { 72 | if (!read_u32(&length)) { 73 | // debug_log("failed to read length", "SHOULD I RETRY?"); 74 | // return false; // comment this if you want retry 75 | continue; // uncomment this if you want retry 76 | } 77 | break; 78 | } 79 | debug_log("length:", length); 80 | debug_log("reading string"); 81 | while (true) { 82 | if (!read_string(str, length)) { 83 | // debug_log("failed to read string", "SHOULD I RETRY?"); 84 | // return false; // comment this if you want retry 85 | continue; // uncomment this if you want retry 86 | } 87 | break; 88 | } 89 | debug_log("read string: [" + str + "]"); 90 | // debug_log(str.length()); 91 | return true; 92 | } 93 | 94 | bool send_message(const std::string& str) { 95 | //debug_log("writing length"); 96 | while (!write_u32(str.length())) { 97 | debug_log("failed to write length, for str:", str, "WILL RETRY"); 98 | } 99 | //debug_log("writing string"); 100 | while (!write_string(str)) { 101 | debug_log("failed to write string, for str:", str, "WILL RETRY"); 102 | } 103 | //debug_log("flushing"); 104 | while (std::fflush(stdout) != 0) { 105 | debug_log("failed to flush, for str:", str, "WILL RETRY"); 106 | } 107 | return true; 108 | } 109 | 110 | int main(void) { 111 | debug_log("startup"); 112 | 113 | std::string payload_str; 114 | while (get_message(payload_str)) { 115 | debug_log("payload_str:", payload_str); 116 | if (payload_str == "\"ping\"") { 117 | send_message("\"pong\""); 118 | } 119 | } 120 | 121 | 122 | debug_log("ending"); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/electron/main.js: -------------------------------------------------------------------------------- 1 | const {app, Menu, Tray, BrowserWindow} = require('electron'); 2 | const path = require('path') 3 | const url = require('url') 4 | const platform = require('os').platform(); 5 | 6 | let win; 7 | function createWindow() { 8 | // Create the browser window. 9 | win = new BrowserWindow({width: 800, height: 600}) 10 | 11 | // and load the index.html of the app. 12 | win.loadURL(url.format({ 13 | pathname: path.join(__dirname, 'index.html'), 14 | protocol: 'file:', 15 | slashes: true 16 | })); 17 | 18 | // Open the DevTools. 19 | win.webContents.openDevTools(); 20 | 21 | // Emitted when the window is closed. 22 | win.on('closed', () => { 23 | // Dereference the window object, usually you would store windows 24 | // in an array if your app supports multi windows, this is the time 25 | // when you should delete the corresponding element. 26 | win = null 27 | }); 28 | } 29 | 30 | function startTray() { 31 | // run in app.on ready - i dont know why but @wadie did it like this 32 | let appIcon = new Tray('icon.' + (platform == 'win32' ? 'ico' : 'png')); 33 | const contextMenu = Menu.buildFromTemplate([ 34 | { 35 | label: 'Screenshots', 36 | type: 'radio' 37 | } 38 | ]); 39 | 40 | // Make a change to the context menu 41 | contextMenu.items[0].checked = false; 42 | 43 | // Call this again for Linux because we modified the context menu 44 | appIcon.setContextMenu(contextMenu); 45 | } 46 | 47 | function startChildProc() { 48 | // run in app.on ready - because i need TextEncoder which comes in from the require statement 49 | const { spawn } = require('child_process'); 50 | let child = spawn('./nativeshot' + (platform == 'win32' ? '.exe' : '')); 51 | 52 | // child.stdin.setEncoding('utf-8'); 53 | // child.stdout.pipe(process.stdout); 54 | 55 | child.stdout.on('data', function (nbuf) { 56 | // nbuf stands for "node buffer" is Buffer which is Uint8Array per http://stackoverflow.com/a/12101012/1828637 57 | console.log('stdout, nbuf:', nbuf, 'nbuf.toJSON:', nbuf.toJSON()); 58 | 59 | // let sizeofuint32 = new Buffer(Uint32Array.of(nbuf.length).buffer).length; 60 | // console.log('sizeofuint32:', sizeofuint32); // is 4 61 | const SIZEOFUINT32 = 4; 62 | 63 | let ix = 0; 64 | let l = nbuf.length; 65 | while(ix < l) { 66 | let lenbuf = Buffer.from(nbuf.buffer, ix, ix + SIZEOFUINT32); // 4 because size of Uint32 is 4. `sizeofuint32` gives 4 67 | console.log('lenbuf:', lenbuf.toJSON()); 68 | // console.log('lenbuf:', lenbuf.toString('utf8')); 69 | let len = new Uint32Array(lenbuf)[0]; 70 | console.log('len:', len); 71 | 72 | let strbuf = Buffer.from(nbuf.buffer, SIZEOFUINT32, len); 73 | console.log('strbuf:', strbuf.toJSON()); 74 | let str = strbuf.toString('utf8'); 75 | console.log('str:', str); 76 | 77 | ix = SIZEOFUINT32 + len; 78 | console.log('post ix:', ix, 'l:', l); 79 | } 80 | }); 81 | 82 | 83 | console.log('Hey there'); 84 | let message = JSON.stringify('ping'); 85 | 86 | // https://dxr.mozilla.org/mozilla-central/source/toolkit/components/extensions/NativeMessaging.jsm#252 87 | let nbuf = Buffer.from(message, 'utf8'); 88 | console.log('nbuf:', nbuf); 89 | console.log('nbuf.length:', nbuf.length); 90 | // https://dxr.mozilla.org/mozilla-central/source/toolkit/components/extensions/NativeMessaging.jsm#298 91 | let lenbuf = new Buffer(Uint32Array.of(nbuf.length).buffer); 92 | console.log('lenbuf:', lenbuf, 'length:', lenbuf.length); 93 | 94 | child.stdin.write(lenbuf); 95 | child.stdin.write(nbuf); 96 | // child.stdin.end(); // otherwise next message wont write, must do this on close of child 97 | 98 | setTimeout(function() { 99 | console.log('ok will send ping again'); 100 | child.stdin.write(lenbuf); 101 | child.stdin.write(nbuf); 102 | // child.stdin.end(); 103 | }, 5000); 104 | } 105 | 106 | function readyHandler() { 107 | startTray(); 108 | startChildProc(); 109 | createWindow(); 110 | } 111 | 112 | function activateHandler() { 113 | // On macOS it's common to re-create a window in the app when the 114 | // dock icon is clicked and there are no other windows open. 115 | if (win === null) { 116 | createWindow(); 117 | } 118 | } 119 | 120 | function allwinClosedHandler() { 121 | // Quit when all windows are closed. 122 | // On macOS it is common for applications and their menu bar 123 | // to stay active until the user quits explicitly with Cmd + Q 124 | if (process.platform !== 'darwin') { 125 | app.quit() 126 | } 127 | } 128 | 129 | app.on('ready', readyHandler); 130 | app.on('activate', activateHandler); 131 | app.on('window-all-closed', allwinClosedHandler); 132 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* README 2 | * --txtype fxhyb can be omitted, its default 3 | * gulp --txtype fxhyb - will create dev vresion, with console logs 4 | * gulp --prod --txtype fxhyb - this will create release version, without console logs 5 | * gulp watch --txtype fxhyb - this will watch for changes 6 | */ 7 | 8 | // Include gulp 9 | var gulp = require('gulp'); 10 | 11 | // Include core modules 12 | var fs = require('fs'); 13 | var path = require('path'); 14 | 15 | // Include Our Plugins 16 | var babel = require('gulp-babel'); 17 | var clean = require('gulp-clean'); 18 | var contains = require('gulp-contains'); 19 | var gulpif = require('gulp-if'); 20 | var gulp_src_ordered = require('gulp-src-ordered-globs'); // http://stackoverflow.com/a/40206149/1828637 21 | var insert = require('gulp-insert'); 22 | var jshint = require('gulp-jshint'); 23 | var jsobfuscator = require('gulp-js-obfuscator'); 24 | var jscrambler = require('gulp-jscrambler'); 25 | var replace = require('gulp-replace'); 26 | var rename = require('gulp-rename'); 27 | var util = require('gulp-util'); 28 | var zip = require('gulp-zip'); 29 | 30 | // Command line options 31 | var options = { // defaults 32 | production: false, // production 33 | // clarg == --prod 34 | // strips the console messages // production/release 35 | txtype: 'fxhyb' // transpile type 36 | // clarg == --txtype BLAH 37 | // values: 38 | // fxhyb == firefox-webextension-hybrid 39 | // fxext == firefox-webextension 40 | // web == web 41 | // affects taskcopy-3rdjs - where to copy the babel-polyfill too 42 | }; 43 | 44 | var clargs = process.argv.slice(2); 45 | var clargs = clargs.map(el => el.toLowerCase().trim()); 46 | 47 | console.log('clargs:', clargs); 48 | // production? 49 | if (clargs.indexOf('--prod') > -1) { 50 | options.production = true; 51 | } 52 | 53 | // txtype? 54 | var ix_txtype = clargs.indexOf('--txtype'); 55 | if (ix_txtype > -1) { 56 | options.txtype = clargs[++ix_txtype]; 57 | } 58 | 59 | // start async-proc9939 60 | gulp.task('clean', function() { 61 | return gulp.src('dist', { read:false }) 62 | .pipe(clean()); 63 | }); 64 | 65 | gulp.task('copy-zip-exe', ['clean'], function() { 66 | if (options.txtype == 'web') { 67 | // do nothing 68 | return gulp.src('.').pipe(util.noop()); 69 | } else { 70 | var dest; 71 | var srcwebext; 72 | switch (options.txtype) { 73 | case 'fxhyb': 74 | dest = 'dist/webextension/exe'; 75 | srcwebext = 'src/webextension'; 76 | break; 77 | default: 78 | dest = 'dist/exe'; 79 | srcwebext = 'src'; 80 | } 81 | 82 | var addonname = JSON.parse(fs.readFileSync(srcwebext + '/_locales/en-US/messages.json', 'utf8')).addon_name.message; 83 | return gulp_src_ordered([ 84 | '../' + addonname + 'Exe/**/*', 85 | '!../' + addonname + 'Exe/**/*.*', 86 | '../' + addonname + 'Exe/**/*.exe' 87 | ]) 88 | .pipe(rename(function(file) { 89 | file.dirname = file.dirname.split(path.sep)[0]; 90 | })) 91 | .pipe(gulp.dest(dest)); 92 | // if not fxhyb then replaces executables with zipped version 93 | } 94 | }); 95 | 96 | gulp.task('copy', ['copy-zip-exe'], function() { 97 | // copy all files but js 98 | return gulp_src_ordered([ 99 | 'src/**/*', 100 | '!src/.*', // no hidden files/dirs in src 101 | '!src/.*/**/*', // no files/dirs in hidden dirs in src 102 | '!src/**/*.js', // no js files from src 103 | '!src/webextension/exe/**/*', // no exe folder 104 | 'src/**/3rd/*.js' // make sure to get 3rd party js files though 105 | ]) 106 | .pipe(gulp.dest('dist')); 107 | }); 108 | 109 | gulp.task('import-3rdjs', ['copy'], function() { 110 | // bring in babel-polyfill to 3rd party directory - determined by clarg txtype 111 | 112 | var dest; 113 | // switch (options.txtype) { 114 | // case 'fxhyb': 115 | // dest = 'dist/webextension/scripts/3rd'; 116 | // break; 117 | // case 'web': 118 | // case 'fxext': 119 | // dest = 'dist/scripts/3rd'; 120 | // break; 121 | // } 122 | 123 | if (fs.existsSync('dist/webextension/scripts/3rd')) { 124 | // options.txtype == fxhyb 125 | dest = 'dist/webextension/scripts/3rd'; 126 | } else if (fs.existsSync('dist/scripts/3rd')) { 127 | // options.txtype == web || fxext 128 | dest = 'dist/scripts/3rd'; 129 | } else { 130 | throw new Error('dont know where to import 3rd party scripts too!'); 131 | } 132 | console.log('dest:', dest); 133 | 134 | return gulp.src([ 135 | 'node_modules/babel-polyfill/dist/polyfill.min.js', 136 | 'node_modules/react/dist/react-with-addons.min.js', 137 | 'node_modules/react-dom/dist/react-dom.min.js', 138 | 'node_modules/redux/dist/redux.min.js', 139 | 'node_modules/react-redux/dist/react-redux.min.js', 140 | 'node_modules/react-router/umd/ReactRouter.min.js' 141 | ]) 142 | .pipe(gulp.dest(dest)); 143 | }); 144 | 145 | gulp.task('initial-tx-js', ['import-3rdjs'], function() { 146 | return gulp.start('tx-then-xpi'); 147 | }); 148 | // end async-proc9939 149 | 150 | // start - standalone3888 - is standalone because so `gulp watch` can trigger this without triggering the clean and copy stuff from above 151 | gulp.task('tx-js', function() { 152 | // tx-js stands for transform-javascripts 153 | 154 | var include_contents = {}; // to avoid multi readFileSync on same file path 155 | 156 | return gulp_src_ordered(['src/**/*.js', '!src/**/3rd/*']) 157 | .pipe(gulpif(options.production, replace(/^.*?console\.(warn|info|log|error|exception|time|timeEnd|jsm).*?$/mg, ''))) 158 | .pipe(replace(/\/\/ #include '([^']+)'/gm, function($0, $1) { 159 | // $1 - ([^']+) - path to file to include 160 | if (!include_contents[$1]) { 161 | include_contents[$1] = fs.readFileSync($1, 'utf8'); 162 | if (options.production) { 163 | include_contents[$1] = include_contents[$1].replace(/^.*?console\.(warn|info|log|error|exception|time|timeEnd|jsm).*?$/mg, ''); 164 | } 165 | if ($1 == 'node_modules/babel-polyfill/dist/polyfill.min.js') { 166 | include_contents[$1] = 'var global = this;\n' + include_contents[$1]; 167 | } 168 | }; 169 | 170 | return '// START INCLUDE - "' + $1 + '"\n' + include_contents[$1] + '// END INCLUDE - "' + $1 + '"'; 171 | })) 172 | .pipe(babel()) 173 | // .pipe(gulpif(options.production, jsobfuscator())) 174 | .pipe(insert.transform(function(contents, file) { 175 | var pathparts = file.path.split(/[\\\/]/); 176 | if (pathparts[pathparts.length-1] == 'bootstrap.js' && pathparts[pathparts.length-2] == 'src') { 177 | var includestr = '// START INCLUDE - babel-polyfill\nvar global = this;\n' + fs.readFileSync('node_modules/babel-polyfill/dist/polyfill.min.js', 'utf8') + '// END INCLUDE - babel-polyfill'; 178 | if (contents.indexOf('\'use strict\';') === 0) { 179 | contents = contents.replace('\'use strict\';', '\'use strict\';\n\n' + includestr); 180 | } else { 181 | contents = includestr + '\n\n' + contents; 182 | } 183 | console.log('ok included babel-polyfill at top'); 184 | } 185 | return contents; 186 | })) 187 | // .pipe(replace(/(^.*?$)([\s\S]*?)\/\/ #includetop-nobabel '([^']+)'/m, function($0, $1, $2, $3) { 188 | // // $1 - (^.*?$) - the "using strict" usually, so first line 189 | // // $2 - ([\s\S]*?) - all lines up till the include line 190 | // // $3 - ([^']+) - path to file to include 191 | // 192 | // if (!include_contents[$3]) { 193 | // include_contents[$3] = fs.readFileSync($3, 'utf8'); 194 | // if (options.production) { 195 | // include_contents[$3] = include_contents[$3].replace(/^.*?console\.(warn|info|log|error|exception|time|timeEnd|jsm).*?$/mg, ''); 196 | // } 197 | // if ($3 == 'node_modules/babel-polyfill/dist/polyfill.min.js') { 198 | // include_contents[$3] = 'var global = this;\n' + include_contents[$3]; 199 | // } 200 | // }; 201 | // 202 | // return $1 + '\n\n// START INCLUDE - "' + $3 + '"\n' + include_contents[$3] + '// END INCLUDE - "' + $3 + '"' + $2; 203 | // })) 204 | .pipe(gulp.dest('dist')); 205 | }); 206 | 207 | gulp.task('tx-then-xpi', ['tx-js'], function() { 208 | if (options.txtype == 'web') { 209 | // do nothing 210 | return gulp.src('.').pipe(util.noop()); 211 | } else { 212 | return gulp.src('dist/**/*') 213 | .pipe(zip('_dist' + Date.now() + '.xpi', { compress:false })) 214 | .pipe(gulp.dest('./')); 215 | } 216 | }); 217 | 218 | gulp.task('xpi', function() { 219 | return gulp.src('dist/**/*') 220 | .pipe(zip('dist.xpi', { compress:false })) 221 | .pipe(gulp.dest('./')); 222 | }); 223 | // end - standalone3888 224 | 225 | 226 | gulp.task('default', ['initial-tx-js']); // copy-3rdjs triggers tx-js 227 | gulp.task('watch', ['initial-tx-js'], function() { 228 | console.log('NOTE: wait for tx-then-xpi to finish, or it may have already finished. as that does the initial js copy'); 229 | // var watcher = gulp.watch('src/**/*.js', ['tx-then-xpi']); 230 | var watcher = gulp.watch('src/**/*', ['initial-tx-js']); 231 | watcher.on('change', function(event) { 232 | console.log('JS file at path "' + event.path + '" was ' + event.type + ', running tx-js...'); 233 | }); 234 | }); 235 | --------------------------------------------------------------------------------