diff --git a/.gitignore b/.gitignore
index 7d5b7a94..16d12354 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/build
/node_modules
+/electron_build
diff --git a/Gulpfile.js b/Gulpfile.js
index 553197d9..64bf6bf2 100644
--- a/Gulpfile.js
+++ b/Gulpfile.js
@@ -47,7 +47,7 @@ gulp.task('tpls', function(){
})
gulp.task('index', function () {
- var files = globby.sync(['build/**/*', '!build/vendor/**/*'])
+ var files = globby.sync(['build/**/*', '!build/vendor/**/*', '!build/electron/**/*'])
var filter = function (files, ext) {
return files.filter(function (file) {
@@ -118,3 +118,5 @@ gulp.task('default', function (cb) {
runSequence('del', 'build', 'watch', cb)
})
+
+require('./gulp-electron')(gulp)
diff --git a/build.config.js b/build.config.js
index 4d42660a..bdc3fda9 100644
--- a/build.config.js
+++ b/build.config.js
@@ -44,6 +44,10 @@ module.exports = {
name:'moment',
src:'node_modules/moment/moment.js',
cdn:'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment.min.js'
+ },
+ {
+ name:'angular-hotkeys',
+ src:'node_modules/angular-hotkeys/build/hotkeys.js'
}
]
}
diff --git a/electron_src/popup/index.html b/electron_src/popup/index.html
new file mode 100644
index 00000000..62339b93
--- /dev/null
+++ b/electron_src/popup/index.html
@@ -0,0 +1,55 @@
+
+
+
+
+ CodeXen App
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/electron_src/popup/popup.js b/electron_src/popup/popup.js
new file mode 100644
index 00000000..5dca4039
--- /dev/null
+++ b/electron_src/popup/popup.js
@@ -0,0 +1,149 @@
+// document.getElementById('search-input').focus()
+
+var remote = require('remote')
+var ipc = require('ipc')
+
+var SEARCH_INPUT = 1
+var RESULT_LIST = 2
+var RESULT_DETAIL = 3
+
+angular.module('codexen.popup', [
+ 'ui.ace',
+ 'satellizer',
+ 'cfp.hotkeys'
+])
+.controller('PopUpController', function ($scope, Snippet, $auth, $window, hotkeys, $document, $filter) {
+
+ // For Dev
+ $scope.toggleDev = function () {
+ remote.getCurrentWindow().toggleDevTools()
+ }
+
+ // Setup Events
+ remote.getCurrentWindow().on('focus', function () {
+ $scope.$apply(focusSearchInput)
+ })
+
+ hotkeys.bindTo($scope)
+ .add('down', function (e) {
+ if ($scope.isFocusing === RESULT_LIST) selectNextItem()
+ e.preventDefault()
+ })
+ .add('up', function (e) {
+ if ($scope.isFocusing === RESULT_LIST) selectPriorItem()
+ e.preventDefault()
+ })
+ .add('right', function (e) {
+ if ($scope.isFocusing === RESULT_LIST) focusDetail()
+ })
+ .add('left', function (e) {
+ if ($scope.isFocusing === RESULT_DETAIL) focusList()
+ })
+ .add('esc', function (e) {
+ switch ($scope.isFocusing) {
+ case RESULT_LIST:
+ focusSearchInput()
+ break
+ case RESULT_DETAIL:
+ focusList()
+ break
+ case SEARCH_INPUT:
+ hidePopUp()
+ }
+ })
+ .add('tab', function (e) {
+ if ($scope.isFocusing === RESULT_LIST) focusDetail()
+
+ })
+
+ // Init Data
+ $scope.snippets = []
+
+ var userId = $auth.getPayload().sub
+ Snippet.findByUser(userId)
+ .success(function (data) {
+ $scope.snippets = data.snippets
+ $scope.selectedItem = $scope.snippets[0]
+ filterList()
+ })
+ function filterList (needle) {
+ $scope.filteredSnippets = $filter('filter')($scope.snippets, needle)
+ }
+ $scope.filterList = filterList
+
+ $scope.isFocusing = 0
+ $scope.selectIndex = 0
+
+ $scope.selectItem = selectItem
+ function selectItem (index) {
+ $scope.selectIndex = index
+ $scope.selectedItem = $scope.filteredSnippets[index]
+ }
+
+ function selectNextItem () {
+ if ($scope.selectIndex >= ($scope.filteredSnippets.length -1)) {
+ return
+ }
+ selectItem(++$scope.selectIndex)
+ }
+
+ function selectPriorItem () {
+ if ($scope.selectIndex == 0) {
+ focusSearchInput()
+ return
+ }
+ selectItem(--$scope.selectIndex)
+ }
+
+ function focusSearchInput () {
+ $scope.isFocusing = SEARCH_INPUT
+ document.getElementById('search-input').focus()
+ }
+
+ function focusList () {
+ $scope.isFocusing = RESULT_LIST
+ document.getElementById('search-input').blur()
+ }
+ $scope.focusList = focusList
+
+ function focusDetail () {
+ $scope.isFocusing = RESULT_DETAIL
+ }
+
+ function hidePopUp () {
+ remote.getCurrentWindow().hide()
+ }
+})
+.directive('resultItem', function () {
+ return {
+ link: function (scope, el, attr) {
+
+ }
+ }
+})
+.directive('searchInput', function () {
+ return {
+ restrict: 'A',
+ link: function (scope, el, attr) {
+ el.on('keydown', function (e) {
+
+ // Down key => Focus on Result list
+ if (e.keyCode === 40) {
+ scope.focusList()
+ e.preventDefault()
+ }
+
+ // Esc key => Dismiss popup
+ if (e.keyCode === 27) {
+ ipc.send('hidePopUp')
+ e.preventDefault()
+ }
+
+ // TODO: Tab key => Auto complete
+ if (e.keyCode === 9) {
+ e.preventDefault()
+ }
+ })
+ }
+ }
+})
diff --git a/electron_src/popup/popup.scss b/electron_src/popup/popup.scss
new file mode 100644
index 00000000..ef1e8856
--- /dev/null
+++ b/electron_src/popup/popup.scss
@@ -0,0 +1,75 @@
+@import "../../src/variables";
+@import "../../src/mixins";
+
+$selected-color: white;
+$selected-bg: $brand-primary;
+
+$focused-shadow-color: $brand-primary;
+
+
+.popup-body{
+ .search-block{
+ padding: 5px;
+ height:44px;
+ position:absolute;
+ top: 0;
+ width: 100%;
+ }
+ .result-block{
+ position:absolute;
+ top: 44px;
+ bottom: 0;
+ width: 100%;
+ overflow: hidden;
+ .result-list{
+ margin: 0;
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 40%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ list-style:none;
+ padding: 0;
+ &.focused{
+ border: solid 1px $brand-primary;
+ }
+ li{
+ a{
+ display:block;
+ padding: 5px 10px;
+ border-bottom: 1px solid $border-color;
+
+ &.selected{
+ color: $selected-color;
+ background-color: $selected-bg;
+ }
+
+ &:hover{
+ }
+ }
+ }
+ border-right: 1px solid $border-color;
+ }
+ .result-detail{
+ &.focused{
+ border: solid 1px $brand-primary;
+ }
+ position: absolute;
+ left: 40%;
+ top: 0;
+ bottom: 0;
+ width: 60%;
+ overflow-y: auto;
+ overflow-x: hidden;
+ .result-detail-cotent{
+ position: absolute;
+ top: 34px;
+ bottom: 0;
+ width: 100%;
+ }
+ }
+ }
+
+}
diff --git a/electron_src/popup/services/snippet.js b/electron_src/popup/services/snippet.js
new file mode 100644
index 00000000..84bafbf7
--- /dev/null
+++ b/electron_src/popup/services/snippet.js
@@ -0,0 +1,55 @@
+/* global angular */
+angular.module('codexen.popup')
+ .constant('apiUrl', 'http://localhost:8000/')
+ .config(function ($authProvider, $httpProvider) {
+ $authProvider.baseUrl = 'http://localhost:8000/'
+
+ $httpProvider.defaults.useXDomain = true
+ delete $httpProvider.defaults.headers.common['X-Requested-With']
+ })
+
+
+angular.module('codexen.popup')
+ .factory('Snippet', function ($http, apiUrl) {
+ var findByUser = function (user) {
+ var url = apiUrl + 'snippets/search'
+
+ return $http.get(url, {
+ params: {
+ user: user
+ }
+ })
+ }
+
+ var create = function (params) {
+ var url = apiUrl + 'snippets/create'
+
+ return $http.post(url, params)
+ }
+
+ var show = function (id) {
+ var url = apiUrl + 'snippets/id/' + id
+
+ return $http.get(url)
+ }
+
+ var update = function (id, params) {
+ var url = apiUrl + 'snippets/id/' + id
+
+ return $http.put(url, params)
+ }
+
+ var destroy = function (id) {
+ var url = apiUrl + 'snippets/id/' + id
+
+ return $http.delete(url)
+ }
+
+ return {
+ findByUser: findByUser,
+ create: create,
+ show: show,
+ delete: destroy,
+ update: update
+ }
+ })
diff --git a/gulp-electron.js b/gulp-electron.js
new file mode 100644
index 00000000..7f1cd38d
--- /dev/null
+++ b/gulp-electron.js
@@ -0,0 +1,125 @@
+var sass = require('gulp-sass')
+var autoprefixer = require('gulp-autoprefixer')
+var templateCache = require('gulp-angular-templatecache')
+var globby = require('globby')
+var template = require('gulp-template')
+var concat = require('gulp-concat')
+var del = require('del')
+var runSequence = require('run-sequence')
+var merge = require('merge-stream')
+
+var changed = require('gulp-changed')
+var cached = require('gulp-cached')
+var remember = require('gulp-remember')
+var livereload = require('gulp-livereload')
+var childProcess = require('child_process')
+
+var config = require('./build.config.js')
+
+// for Dist
+var rev = require('gulp-rev')
+var ngAnnotate = require('gulp-ng-annotate')
+var uglify = require('gulp-uglify')
+var minifyCss = require('gulp-minify-css')
+
+module.exports = function (gulp) {
+
+
+ /*
+ * Electron build
+ */
+ gulp.task('elec-js', function(){
+ var src = gulp.src(['src/**/*.js'])
+ .pipe(changed('electron_build'))
+ .pipe(gulp.dest('electron_build'))
+ var elecSrc = gulp.src(['electron_src/**/*.js'])
+ .pipe(changed('electron_build/electron'))
+ .pipe(gulp.dest('electron_build/electron'))
+ var elecHtml = gulp.src(['electron_src/**/*.html'])
+ .pipe(changed('electron_build/electron'))
+ .pipe(gulp.dest('electron_build/electron'))
+
+ return merge(src, elecSrc, elecHtml)
+ })
+
+ gulp.task('elec-sass', function () {
+ return gulp.src(['src/**/*.scss', 'electron_src/**/*.scss'])
+ .pipe(cached('styles'))
+ .pipe(sass().on('error', sass.logError))
+ .pipe(autoprefixer())
+ .pipe(remember('styles'))
+ .pipe(concat('all.css'))
+ .pipe(gulp.dest('electron_build'))
+ })
+
+ gulp.task('elec-tpls', function(){
+ return gulp.src('src/**/*.tpl.html')
+ .pipe(templateCache())
+ .pipe(gulp.dest('electron_build'))
+ })
+
+ gulp.task('elec-index', function () {
+ var files = globby.sync(['electron_build/**/*', '!electron_build/vendor/**/*', '!electron_build/electron/**/*'])
+
+ var filter = function (files, ext) {
+ return files.filter(function (file) {
+ var reg = new RegExp('.+\.' + ext + '$')
+ return file.match(reg)
+ }).map(function (file) {
+ return file.replace('electron_build/', '')
+ })
+ }
+ var scripts = filter(files, 'js')
+ var styles = filter(files, 'css')
+
+ return gulp.src('src/index.html')
+ .pipe(template({
+ scripts: scripts,
+ styles: styles,
+ env: 'build'
+ }))
+ .pipe(gulp.dest('electron_build'))
+ })
+
+ gulp.task('elec-vendor', function () {
+ var vendors = config.vendors
+
+ var vendorFiles = vendors.map(function (vendor) {
+ return vendor.src
+ })
+
+ return gulp.src(vendorFiles)
+ .pipe(gulp.dest('electron_build/vendor'))
+ })
+
+ gulp.task('elec-resources', function () {
+ return gulp.src('resources/**/*')
+ .pipe(changed('electron_build/resources'))
+ .pipe(gulp.dest('electron_build/resources'))
+ })
+
+ gulp.task('elec-build', function (cb) {
+ runSequence(['elec-js', 'elec-sass', 'elec-tpls', 'elec-vendor', 'elec-resources'], 'elec-index', cb)
+ })
+
+ gulp.task('elec-watch', function (cb) {
+ gulp.watch(['src/**/*.js', 'electron_src/**/*.js', 'electron_src/**/*.html'], ['elec-js'])
+
+ gulp.watch(['src/**/*.scss', 'electron_src/**/*.scss'], ['elec-sass'])
+
+ gulp.watch('src/**/*.tpl.html', ['elec-tpls'])
+
+ gulp.watch(['electron_build/**/*', '!electron_build/vendor/**/*', '!electron_build/electron/**/*'], ['elec-index'])
+
+ livereload.listen()
+ })
+
+ gulp.task('elec-del', function (cb) {
+ del(['electron_build/**/*'], cb)
+ })
+
+ gulp.task('elec', function (cb) {
+ runSequence('elec-del', 'elec-build', 'elec-watch', cb)
+ })
+
+}
diff --git a/main.js b/main.js
index bb1ba6c4..f1106629 100644
--- a/main.js
+++ b/main.js
@@ -8,20 +8,209 @@ require('crash-reporter').start()
// be closed automatically when the javascript object is GCed.
var mainWindow = null
-console.log(process.platform)
-// Quit when all windows are closed.
app.on('window-all-closed', function () {
- if (process.platform !== 'darwin')
- app.quit()
+ if (process.platform !== 'darwin') app.quit()
})
+
app.on('ready', function () {
+
mainWindow = new BrowserWindow({width: 800, height: 600})
- mainWindow.loadUrl('file://' + __dirname + '/build/index.html')
+ mainWindow.loadUrl('file://' + __dirname + '/electron_build/index.html')
- mainWindow.openDevTools()
+ // mainWindow.openDevTools()
mainWindow.on('closed', function () {
+ console.log('main closed')
mainWindow = null
})
+
+ var globalShortcut = require('global-shortcut')
+
+ var popUpWindow = new BrowserWindow({
+ width: 600,
+ height: 400,
+ show: false,
+ frame: false
+ })
+
+ popUpWindow.loadUrl('file://' + __dirname + '/electron_build/electron/popup/index.html')
+
+ app.on('activate-with-no-open-windows', function () {
+ if (mainWindow == null) {
+ console.log('new WIndow!')
+ mainWindow = new BrowserWindow({width: 800, height: 600})
+
+ mainWindow.loadUrl('file://' + __dirname + '/electron_build/index.html')
+
+ mainWindow.on('closed', function () {
+ mainWindow = null
+ })
+ }
+ mainWindow.show()
+ })
+
+ popUpWindow.on('blur', function () {
+ popUpWindow.hide()
+ })
+
+ var hidePopUp = function () {
+ if(fromMain){
+
+ } else {
+ mainWindow.hide()
+ Menu.sendActionToFirstResponder('hide:');
+ }
+
+ popUpWindow.hide()
+ }
+
+ var ipc = require('ipc')
+ ipc.on('hidePopUp', function () {
+ hidePopUp()
+ })
+
+ var fromMain
+ // Register a 'ctrl+x' shortcut listener.
+ var ret = globalShortcut.register('ctrl+tab+shift', function () {
+
+ if (popUpWindow.isVisible()) {
+ hidePopUp()
+ return
+ }
+ fromMain = mainWindow.isFocused()
+ popUpWindow.show()
+
+ })
+ if (!ret) console.log('registerion fails')
+
+
+ // MENU
+ var Menu = require('menu')
+ var template = [
+ {
+ label: 'Electron',
+ submenu: [
+ {
+ label: 'About Electron',
+ selector: 'orderFrontStandardAboutPanel:'
+ },
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Services',
+ submenu: []
+ },
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Hide Electron',
+ accelerator: 'Command+H',
+ selector: 'hide:'
+ },
+ {
+ label: 'Hide Others',
+ accelerator: 'Command+Shift+H',
+ selector: 'hideOtherApplications:'
+ },
+ {
+ label: 'Show All',
+ selector: 'unhideAllApplications:'
+ },
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Quit',
+ accelerator: 'Command+Q',
+ click: function() { app.quit(); }
+ },
+ ]
+ },
+ {
+ label: 'Edit',
+ submenu: [
+ {
+ label: 'Undo',
+ accelerator: 'Command+Z',
+ selector: 'undo:'
+ },
+ {
+ label: 'Redo',
+ accelerator: 'Shift+Command+Z',
+ selector: 'redo:'
+ },
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Cut',
+ accelerator: 'Command+X',
+ selector: 'cut:'
+ },
+ {
+ label: 'Copy',
+ accelerator: 'Command+C',
+ selector: 'copy:'
+ },
+ {
+ label: 'Paste',
+ accelerator: 'Command+V',
+ selector: 'paste:'
+ },
+ {
+ label: 'Select All',
+ accelerator: 'Command+A',
+ selector: 'selectAll:'
+ },
+ ]
+ },
+ {
+ label: 'View',
+ submenu: [
+ {
+ label: 'Reload',
+ accelerator: 'Command+R',
+ click: function() { BrowserWindow.getFocusedWindow().reloadIgnoringCache(); }
+ },
+ {
+ label: 'Toggle DevTools',
+ accelerator: 'Alt+Command+I',
+ click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
+ },
+ ]
+ },
+ {
+ label: 'Window',
+ submenu: [
+ {
+ label: 'Minimize',
+ accelerator: 'Command+M',
+ selector: 'performMiniaturize:'
+ },
+ {
+ label: 'Close',
+ accelerator: 'Command+W',
+ selector: 'performClose:'
+ },
+ {
+ type: 'separator'
+ },
+ {
+ label: 'Bring All to Front',
+ selector: 'arrangeInFront:'
+ },
+ ]
+ },
+ {
+ label: 'Help',
+ submenu: []
+ },
+ ];
+
+ menu = Menu.buildFromTemplate(template);
+
+ Menu.setApplicationMenu(menu);
})
diff --git a/package.json b/package.json
index 4d343b01..38eb81f5 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@rokt33r/angular-ui-ace": "^0.2.3",
"angular": "^1.3.15",
"angular-bootstrap": "^0.12.0",
+ "angular-hotkeys": "^1.4.5",
"angular-md5": "^0.1.7",
"angular-sanitize": "^1.3.15",
"angular-ui-router": "^0.2.15",
diff --git a/src/index.html b/src/index.html
index 98617a69..33c9d9c0 100644
--- a/src/index.html
+++ b/src/index.html
@@ -7,7 +7,7 @@
-
+
Codexen!