├── .gitignore ├── LICENSE ├── README.md ├── app ├── main.js └── package.json ├── assets ├── background.png ├── icon.icns └── icon.ico ├── build.js ├── builder.json ├── package.json └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | app/docs 2 | dist 3 | node_modules 4 | tmp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 by Appcelerator, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | This source code contains patents and patents pending by Appcelerator, Inc. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Appcelerator Docs Desktop App ![EXPERIMENTAL](https://img.shields.io/badge/WARNING-EXPERIMENTAL-red.svg?style=flat-square) 2 | Desktop Application for Mac, Windows and Linux to use the [Appcelerator Documentation](http://docs.appcelerator.com) offline. Uses GitHub's [Electron](http://electron.atom.io/). 3 | 4 | ![screenshot](screenshot.png) 5 | 6 | ## Install 7 | 8 | Get the installers via [Releases](https://github.com/appcelerator-developer-relations/appc-docs-desktop/releases). 9 | 10 | ## Build 11 | Instructions to update and build the app from source. 12 | 13 | ### Prerequisites 14 | 15 | * Max OS X 16 | * NodeJS 0.12 17 | * Wine: 18 | 19 | brew update && brew upgrade 20 | brew install Caskroom/cask/xquartz 21 | brew install wine makensis 22 | 23 | ### Steps 24 | 25 | 1. Clone (a fork of) this repository 26 | 2. Install dependencies: `npm install` 27 | 3. Run `npm run build` to download the latest CI docs, build the apps and create the installers 28 | 4. Create a release and attach the files in [dist](dist). 29 | 30 | ## Test 31 | You can use `node build ` to run individual steps of the build process when you are debugging errors. 32 | 33 | ## License 34 | See [LICENSE](LICENSE). 35 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | var app = require('app'); // Module to control application life. 2 | var BrowserWindow = require('browser-window'); // Module to create native browser window. 3 | var Menu = require('menu'); 4 | 5 | // Report crashes to our server. 6 | require('crash-reporter').start(); 7 | 8 | // Keep a global reference of the window object, if you don't, the window will 9 | // be closed automatically when the JavaScript object is garbage collected. 10 | var mainWindow = null; 11 | 12 | // Quit when all windows are closed. 13 | app.on('window-all-closed', function () { 14 | // On OS X it is common for applications and their menu bar 15 | // to stay active until the user quits explicitly with Cmd + Q 16 | if (process.platform !== 'darwin') { 17 | app.quit(); 18 | } 19 | }); 20 | 21 | // This method will be called when Electron has finished 22 | // initialization and is ready to create browser windows. 23 | app.on('ready', function () { 24 | 25 | var template = [{ 26 | label: 'Edit', 27 | submenu: [{ 28 | label: 'Undo', 29 | accelerator: 'CmdOrCtrl+Z', 30 | role: 'undo' 31 | }, { 32 | label: 'Redo', 33 | accelerator: 'Shift+CmdOrCtrl+Z', 34 | role: 'redo' 35 | }, { 36 | type: 'separator' 37 | }, { 38 | label: 'Cut', 39 | accelerator: 'CmdOrCtrl+X', 40 | role: 'cut' 41 | }, { 42 | label: 'Copy', 43 | accelerator: 'CmdOrCtrl+C', 44 | role: 'copy' 45 | }, { 46 | label: 'Paste', 47 | accelerator: 'CmdOrCtrl+V', 48 | role: 'paste' 49 | }, { 50 | label: 'Select All', 51 | accelerator: 'CmdOrCtrl+A', 52 | role: 'selectall' 53 | }] 54 | }, { 55 | label: 'View', 56 | submenu: [{ 57 | label: 'Reload', 58 | accelerator: 'CmdOrCtrl+R', 59 | click: function (item, focusedWindow) { 60 | if (focusedWindow) { 61 | focusedWindow.reload(); 62 | } 63 | } 64 | }, { 65 | label: 'Toggle Full Screen', 66 | accelerator: (function () { 67 | if (process.platform === 'darwin') { 68 | return 'Ctrl+Command+F'; 69 | } else { 70 | return 'F11'; 71 | } 72 | })(), 73 | click: function (item, focusedWindow) { 74 | if (focusedWindow) { 75 | focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); 76 | } 77 | } 78 | }, { 79 | label: 'Toggle Developer Tools', 80 | accelerator: (function () { 81 | if (process.platform === 'darwin') { 82 | return 'Alt+Command+I'; 83 | } else { 84 | return 'Ctrl+Shift+I'; 85 | } 86 | })(), 87 | click: function (item, focusedWindow) { 88 | if (focusedWindow) { 89 | focusedWindow.toggleDevTools(); 90 | } 91 | } 92 | }, ] 93 | }, { 94 | label: 'Window', 95 | role: 'window', 96 | submenu: [{ 97 | label: 'Minimize', 98 | accelerator: 'CmdOrCtrl+M', 99 | role: 'minimize' 100 | }, { 101 | label: 'Close', 102 | accelerator: 'CmdOrCtrl+W', 103 | role: 'close' 104 | }, ] 105 | }, { 106 | label: 'Help', 107 | role: 'help', 108 | submenu: [{ 109 | label: 'Online Documentation', 110 | click: function () { 111 | require('shell').openExternal('http://docs.appcelerator.com/platform/latest/'); 112 | } 113 | }, { 114 | label: 'Report an Issue...', 115 | click: function () { 116 | require('shell').openExternal('https://github.com/appcelerator-developer-relations/appc-docs-desktop/issues'); 117 | } 118 | }] 119 | }, ]; 120 | 121 | if (process.platform === 'darwin') { 122 | var name = app.getName(); 123 | template.unshift({ 124 | label: name, 125 | submenu: [{ 126 | label: 'About ' + name, 127 | role: 'about' 128 | }, { 129 | type: 'separator' 130 | }, { 131 | label: 'Services', 132 | role: 'services', 133 | submenu: [] 134 | }, { 135 | type: 'separator' 136 | }, { 137 | label: 'Hide ' + name, 138 | accelerator: 'Command+H', 139 | role: 'hide' 140 | }, { 141 | label: 'Hide Others', 142 | accelerator: 'Command+Shift+H', 143 | role: 'hideothers' 144 | }, { 145 | label: 'Show All', 146 | role: 'unhide' 147 | }, { 148 | type: 'separator' 149 | }, { 150 | label: 'Quit', 151 | accelerator: 'Command+Q', 152 | click: function () { 153 | app.quit(); 154 | } 155 | }, ] 156 | }); 157 | // Window menu. 158 | template[3].submenu.push({ 159 | type: 'separator' 160 | }, { 161 | label: 'Bring All to Front', 162 | role: 'front' 163 | }); 164 | } 165 | 166 | Menu.setApplicationMenu(Menu.buildFromTemplate(template)); 167 | 168 | // Create the browser window. 169 | mainWindow = new BrowserWindow({ 170 | width: 1024, 171 | height: 768 172 | }); 173 | 174 | // and load the index.html of the app. 175 | mainWindow.loadUrl('file://' + __dirname + '/docs/latest/index.html'); 176 | 177 | // Open the DevTools. 178 | // mainWindow.openDevTools(); 179 | 180 | // Emitted when the window is closed. 181 | mainWindow.on('closed', function () { 182 | // Dereference the window object, usually you would store windows 183 | // in an array if your app supports multi windows, this is the time 184 | // when you should delete the corresponding element. 185 | mainWindow = null; 186 | }); 187 | }); 188 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Appcelerator Docs", 3 | "version" : "1.0.0", 4 | "main" : "main.js" 5 | } -------------------------------------------------------------------------------- /assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-labs/appc-docs-desktop/845148218636b4b394527142538facc0bfa459a0/assets/background.png -------------------------------------------------------------------------------- /assets/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-labs/appc-docs-desktop/845148218636b4b394527142538facc0bfa459a0/assets/icon.icns -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-labs/appc-docs-desktop/845148218636b4b394527142538facc0bfa459a0/assets/icon.ico -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | var child_process = require('child_process'); 2 | var path = require('path'); 3 | 4 | var async = require('async'); 5 | var builder = require('electron-builder').init(); 6 | var fs = require('fs-extra'); 7 | var packager = require('electron-packager'); 8 | var ProgressBar = require('progress'); 9 | var request = require('request'); 10 | var rimraf = require('rimraf'); 11 | var unzip = require('unzip'); 12 | 13 | var pkg = require('./package.json'); 14 | var config = require('./builder.json'); 15 | 16 | exports.cleanTmp = function (callback) { 17 | rimraf('tmp', callback); 18 | }; 19 | 20 | exports.cleanDist = function (callback) { 21 | rimraf('dist', callback); 22 | }; 23 | 24 | exports.downloadNewest = function (callback) { 25 | var url = 'http://builds.appcelerator.com.s3.amazonaws.com/mobile/docs/index.json'; 26 | 27 | console.info('Fetching list of CI builds..'); 28 | console.info(url); 29 | 30 | request(url, function (error, response, body) { 31 | 32 | if (error || response.statusCode !== 200) { 33 | return callback(error || response.statusCode); 34 | } 35 | 36 | var list; 37 | 38 | try { 39 | list = JSON.parse(body); 40 | } catch (e) { 41 | return callback(e); 42 | } 43 | 44 | var newest = list.pop(); 45 | var filepath = 'tmp/' + newest.filename; 46 | 47 | fs.stat(filepath, function (err, stats) { 48 | 49 | if (!err && stats.isFile()) { 50 | console.info('Skipping download because we already did.'); 51 | return callback(null, filepath); 52 | } 53 | 54 | var url = 'http://builds.appcelerator.com.s3.amazonaws.com/mobile/docs/' + newest.filename; 55 | 56 | console.info('Downloading latest CI build..'); 57 | console.info(url); 58 | 59 | fs.ensureDirSync(path.dirname(filepath)); 60 | 61 | request 62 | .get(url) 63 | .on('response', function (res) { 64 | var len = parseInt(res.headers['content-length'], 10); 65 | 66 | console.log(); 67 | 68 | var bar = new ProgressBar('[:bar] :percent :etas', { 69 | complete: '=', 70 | incomplete: ' ', 71 | width: 20, 72 | total: len 73 | }); 74 | 75 | res.on('data', function (chunk) { 76 | bar.tick(chunk.length); 77 | }); 78 | 79 | res.on('end', function () { 80 | console.log('\n'); 81 | }); 82 | }) 83 | .on('error', callback) 84 | .on('end', function () { 85 | console.info('Extracting download..'); 86 | 87 | fs.createReadStream(filepath) 88 | .on('error', callback) 89 | .on('end', function () { 90 | callback(null); 91 | }) 92 | .pipe(unzip.Extract({ 93 | path: 'tmp/app/docs' 94 | })); 95 | 96 | }) 97 | .pipe(fs.createWriteStream(filepath)); 98 | }); 99 | }); 100 | }; 101 | 102 | exports.copyApp = function (callback) { 103 | console.info('Copying app to tmp/app..'); 104 | 105 | fs.copy('app', 'tmp/app', callback); 106 | }; 107 | 108 | exports.packageAll = function (callback) { 109 | 110 | console.info('Packaging..'); 111 | 112 | packager({ 113 | dir: 'tmp/app', 114 | name: config.osx.title, 115 | out: 'tmp/builds', 116 | version: pkg.dependencies['electron-prebuilt'].substr(1), 117 | all: true, 118 | icon: 'assets/icon', 119 | }, function (err, packages) { 120 | 121 | if (err) { 122 | return callback(err); 123 | } 124 | 125 | fs.ensureDirSync('dist'); 126 | 127 | console.info('Compressing linux-ia32..'); 128 | 129 | child_process.execFile('tar', ['-czf', path.join(__dirname, 'dist', config.osx.title + ' linux-ia32.tar.gz'), config.osx.title + '-linux-ia32'], { 130 | cwd: 'tmp/builds' 131 | }, function (error, stdout, stderr) { 132 | 133 | if (error) { 134 | return callback(error); 135 | } 136 | 137 | console.info('Compressing linux-x64..'); 138 | 139 | child_process.execFile('tar', ['-czf', path.join(__dirname, 'dist', config.osx.title + ' linux-x64.tar.gz'), config.osx.title + '-linux-x64'], { 140 | cwd: 'tmp/builds' 141 | }, function (error, stdout, stderr) { 142 | 143 | if (error) { 144 | return callback(error); 145 | } 146 | 147 | return callback(); 148 | 149 | }); 150 | 151 | }); 152 | 153 | }); 154 | }; 155 | 156 | exports.buildOSX = function (callback) { 157 | 158 | console.info('Building darwin-x64..'); 159 | 160 | builder.build({ 161 | appPath: path.resolve('tmp', 'builds', config.osx.title + '-darwin-x64', config.osx.title + '.app'), 162 | platform: 'osx', 163 | out: 'tmp/builds', 164 | config: 'builder.json' 165 | }, function (err, res) { 166 | 167 | if (err) { 168 | return callback(err); 169 | } 170 | 171 | fs.move(path.join('tmp', 'builds', config.osx.title + '.dmg'), path.join('dist', config.osx.title + ' darwin-x64.dmg'), callback); 172 | }); 173 | }; 174 | 175 | exports.buildWin32 = function (callback) { 176 | 177 | console.info('Building win32-ia32..'); 178 | 179 | builder.build({ 180 | appPath: path.resolve('tmp', 'builds', config.osx.title + '-win32-ia32'), 181 | platform: 'win', 182 | out: 'tmp/builds', 183 | config: 'builder.json' 184 | 185 | }, function (err, res) { 186 | 187 | if (err) { 188 | return callback(err); 189 | } 190 | 191 | fs.move(path.join('tmp', 'builds', config.osx.title + ' Setup.exe'), path.join('dist', config.osx.title + ' win32-ia32.exe'), callback); 192 | }); 193 | }; 194 | 195 | exports.buildWin64 = function (callback) { 196 | 197 | console.info('Building win32-x64..'); 198 | 199 | builder.build({ 200 | appPath: path.resolve('tmp', 'builds', config.osx.title + '-win32-x64'), 201 | platform: 'win', 202 | out: 'tmp/builds', 203 | config: 'builder.json' 204 | 205 | }, function (err, res) { 206 | 207 | if (err) { 208 | return callback(err); 209 | } 210 | 211 | fs.move(path.join('tmp', 'builds', config.osx.title + ' Setup.exe'), path.join('dist', config.osx.title + ' win32-x64.exe'), callback); 212 | }); 213 | }; 214 | 215 | exports.run = function (callback) { 216 | 217 | child_process.execFile('./node_modules/.bin/electron', ['./tmp/app'], {}, function (error, stdout, stderr) { 218 | 219 | if (error) { 220 | return callback(error); 221 | } 222 | 223 | return callback(); 224 | 225 | }); 226 | }; 227 | 228 | var tasks = []; 229 | var input = process.argv.slice(2); 230 | 231 | if (input.length > 0) { 232 | 233 | input.forEach(function (task) { 234 | 235 | if (task === 'clean') { 236 | tasks.push(exports.cleanTmp, exports.cleanDist); 237 | 238 | } else if (task === 'dev') { 239 | tasks.push(exports.copyApp, exports.run); 240 | 241 | } else if (task === 'build') { 242 | tasks.push(exports.buildOSX, exports.buildWin64, exports.buildWin32); 243 | 244 | } else if (exports[task]) { 245 | tasks.push(exports[task]); 246 | 247 | } else { 248 | console.error('Cannot find task: ' + task); 249 | process.exit(1); 250 | } 251 | 252 | }); 253 | 254 | } else { 255 | tasks = [ 256 | exports.cleanTmp, 257 | exports.cleanDist, 258 | exports.downloadNewest, 259 | exports.copyApp, 260 | exports.packageAll, 261 | exports.buildOSX, 262 | exports.buildWin64, 263 | exports.buildWin32 264 | ]; 265 | } 266 | 267 | async.series(tasks, function (err, results) { 268 | 269 | if (err) { 270 | console.error(err); 271 | process.exit(1); 272 | } 273 | 274 | console.info('Done!'); 275 | 276 | }); 277 | -------------------------------------------------------------------------------- /builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "osx" : { 3 | "title": "Appcelerator Docs", 4 | "background": "assets/background.png", 5 | "icon": "assets/icon.icns", 6 | "icon-size": 80, 7 | "contents": [ 8 | { "x": 320, "y": 150, "type": "link", "path": "/Applications" }, 9 | { "x": 160, "y": 155, "type": "file" } 10 | ] 11 | }, 12 | "win" : { 13 | "title" : "Appcelerator Docs", 14 | "icon" : "assets/icon.ico" 15 | } 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appc-docs-desktop", 3 | "version": "5.0.0", 4 | "description": "Appcelerator Documentation", 5 | "main": "build.js", 6 | "scripts": { 7 | "build": "node build.js" 8 | }, 9 | "dependencies": { 10 | "async": "^1.4.2", 11 | "electron-builder": "^2.0.0", 12 | "electron-packager": "^5.1.0", 13 | "electron-prebuilt": "^0.33.1", 14 | "fs-extra": "^0.24.0", 15 | "progress": "^1.1.8", 16 | "request": "^2.63.0", 17 | "rimraf": "^2.4.3", 18 | "unzip": "^0.1.11" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appcelerator-labs/appc-docs-desktop/845148218636b4b394527142538facc0bfa459a0/screenshot.png --------------------------------------------------------------------------------