diff --git a/.gitignore b/.gitignore index 3c3629e..ade14b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.DS_Store +npm-debug.log node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index 71385ae..2e385cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,6 @@ ## 0.1.0 - First Release * Enable to turn on/off LiveReload server + +## 0.3.0 +* Works with Atom 1.0 +* Supports some preferences diff --git a/LICENSE.md b/LICENSE.md index b2ea47a..546f52d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2014 Taegon Kim +Copyright (c) 2014-2015 Taegon Kim Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 5aba786..b05127a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ An [Atom](https://atom.io/) package enables you to control built-in [LiveReload] > *from LiveReload official site* ## Usage -1. Select main menu `Packages > LiveReload > Toggle Server` to turn the server on. Or, you can click `Toggle LiveReload` in the context menu to do same thing. +1. Select main menu `Packages > LiveReload > Toggle Server` to turn the server on. Or press `Ctrl+Shift+R`. 2. When the server started successfully, `LiveReload: PORT_NUMBER` text appears in the status bar(see the bottom right panel in the following screenshot). ![LiveReload status text](https://cloud.githubusercontent.com/assets/212034/3565696/c50f01ce-0aca-11e4-991e-4cb8475364c4.png) 3. Click it to get the url of `livereload.js` JavaScript file. @@ -17,6 +17,7 @@ An [Atom](https://atom.io/) package enables you to control built-in [LiveReload] ## Features * Reloads web pages when any file is saved. * Applies changes without reloading when any CSS or image changed. +* Works well with [Chrome LiveReload extension](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en). ## Todo * Support preprocessors. diff --git a/keymaps/livereload.cson b/keymaps/livereload.cson deleted file mode 100644 index fe6485b..0000000 --- a/keymaps/livereload.cson +++ /dev/null @@ -1,14 +0,0 @@ -# Keybindings require three things to be fully defined: A selector that is -# matched against the focused element, the keystroke and the command to -# execute. -# -# Below is a basic keybinding which registers on all platforms by applying to -# the root workspace element. - -# For more detailed documentation see -# https://atom.io/docs/latest/advanced/keymaps - -'.platform-darwin': - 'ctrl-shift-R': 'livereload:toggle' -'.platform-win32': - 'ctrl-shift-R': 'livereload:toggle' diff --git a/keymaps/livereload.json b/keymaps/livereload.json new file mode 100644 index 0000000..4842c66 --- /dev/null +++ b/keymaps/livereload.json @@ -0,0 +1,5 @@ +{ + "atom-workspace": { + "ctrl-shift-r": "livereload:toggle" + } +} diff --git a/lib/livereload-view.coffee b/lib/livereload-view.coffee deleted file mode 100644 index 48944e9..0000000 --- a/lib/livereload-view.coffee +++ /dev/null @@ -1,91 +0,0 @@ -{View} = require 'atom' -livereload = require 'livereload' - -defaultPort = 35729 - -module.exports = -class LivereloadView extends View - server: null # livereload server - port: defaultPort - tooltip: '' - - @content: -> - @div class: 'status-stats inline-block livereload', => - @a outlet: 'scriptLink', href: '#' - - initialize: (serializeState) -> - atom.workspaceView.command 'livereload:toggle', => @toggle() - @attach() - # little hack to show dynamic tooltip text - @scriptLink - .setTooltip '' - .data 'bs.tooltip' - .options.title = => @tooltip - - # Returns an object that can be retrieved when package is activated - serialize: -> - - # Tear down any state and detach - destroy: -> - @detach() - @closeServer() - - attach: -> - {statusBar} = atom.workspaceView - if statusBar? - statusBar.prependRight this - - # add event - @scriptLink - .on 'click', (event) => - event.preventDefault() - atom.clipboard.write event.target.getAttribute 'url' - @tooltip = 'URL copied' - @scriptLink.tooltip 'show' - .on 'mouseenter', (event) => - @tooltip = 'Click to copy the URL to clipboard' - @scriptLink.tooltip 'show' - .on 'mouseleave', (event) => - @scriptLink.tooltip 'hide' - - startServer: -> - @find('a').text('LiveReload: ...').removeAttr('title').removeAttr('url') - - @server = livereload.createServer { - port: @port, - exclusions: ['.DS_Store'] - } - - @server.config.server - .on 'error', (err) => - if err.code == 'EADDRINUSE' - console.log "LiveReload: port #{@port} already in use. Trying port #{++@port}..." - - try @server.close() - @server = null - - setTimeout @startServer.bind(@), 1000 - .on 'listening', () => - console.log "LiveReload: listening on port #{@port}." - - @port = defaultPort - @find 'a' - .text "LiveReload: #{@port}" - .attr 'url', "http://localhost:#{@port}/livereload.js" - - if path = atom.project.getPath() - @server.watch path - - closeServer: -> - try - @server.config.server.close () => - @find 'a' - .text '' - .removeAttr 'url' - @server = null - - toggle: -> - if @server - @closeServer() - else - @startServer() diff --git a/lib/livereload-view.js b/lib/livereload-view.js new file mode 100644 index 0000000..7caafc3 --- /dev/null +++ b/lib/livereload-view.js @@ -0,0 +1,140 @@ +"use babel"; + +import {CompositeDisposable} from 'atom'; +import livereload from 'livereload'; +import _ from 'lodash'; + +const DEFAULT_EXTS = 'html css js png gif jpg php php5 py rb erb coffee'.split(' '); +const DEFAULT_EXCLUSIONS = '.git/ .svn/ .hg/'.split(' '); + +class LivereloadView extends HTMLDivElement { + server = null; + subscriptions = null; + tooltipText = 'hello'; + + initialize(state) { + // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable + this.subscriptions = new CompositeDisposable(); + + // add content + this.innerHTML = ''; + this.firstChild.addEventListener('click', (event) => this.handleClick(event), false); + + this.classList.add('livereload-status', 'inline-block'); + } + + loadConfig() { + let ret = {}; + + // port number + ret.port = atom.config.get('livereload.port'); + + // use HTTPS + ret.https = atom.config.get('livereload.useHTTPS') ? {} : null; + + // applyJSLive and applyCSSLive + ret.applyJSLive = atom.config.get('livereload.applyJSLive'); + ret.applyCSSLive = atom.config.get('livereload.applyCSSLive'); + + // exts + let exts = atom.config.get('livereload.exts').split(',').map( ext => ext.trim() ); + exts = _.difference(exts, DEFAULT_EXTS); + exts = _.uniq(exts); + ret.exts = exts; + + let exclusions = atom.config.get('livereload.exclusions').split(',').map( ex => ex.trim() ); + exclusions = exclusions.concat(['.DS_Store', '.gitignore']); + exclusions = _.difference(exclusions, DEFAULT_EXCLUSIONS); + exclusions = _.uniq(exclusions); + ret.exclusions = exclusions; + + return ret; + } + + attach() { + // Register command that toggles this view + this.subscriptions.add( + atom.commands.add( 'atom-workspace', { 'livereload:toggle': this.toggle.bind(this) } ) + ); + + // tooltip + this.subscriptions.add( + atom.tooltips.add( this, {title: () => this.tooltipText} ) + ); + } + + detach() { + this.subscriptions.dispose(); + } + + serialize() { + + } + + destroy() { + try { this.detach() } catch(e){}; + + this.subscriptions = null; + this.remove(); + } + + toggle() { + if (this.server) { + this.closeServer(); + } else { + this.startServer(); + } + } + + handleClick(event) { + event.preventDefault(); + if (this.firstChild.dataset.url) { + atom.clipboard.write(this.firstChild.dataset.url, 'url'); + } + } + + startServer() { + this.firstChild.dataset.url = ''; + this.firstChild.textContent = 'LiveReload: ...'; + + // load configurations + let config = this.loadConfig(); + + // create a server + this.server = livereload.createServer(config); + + this.server.config.server + .on('error', (err) => { + if (err.code === 'EADDRINUSE') { + this.tooltipText = `Trying port ${config.port+1}...`; + console.log(`LiveReload: port ${config.port} already in use. Trying port ${config.port+1}...`); + config.port++; + + try { this.server.close(); } catch(e) {}; + this.server = null; + + setTimeout( this.startServer.bind(this), 1000 ); + } + }) + .on('listening', () => { + console.log(`LiveReload: listening on port ${config.port}.`); + + this.firstChild.textContent = `LiveReload: ${config.port}`; + this.tooltipText = 'Click to copy the URL to clipboard'; + this.firstChild.dataset.url = (config.useHTTPS ? 'https':'http') + `://localhost:${config.port}/livereload.js`; + + let paths = atom.project.getPaths(); + this.server.watch(paths); + }); + } + + closeServer() { + this.firstChild.textContent = 'LiveReload: Off'; + this.server.config.server.close(); + this.server = null; + } +} + +var LivereloadViewTag = document.registerElement('livereload-status-bar', {prototype:LivereloadView.prototype}); + +export default LivereloadViewTag; diff --git a/lib/livereload.coffee b/lib/livereload.coffee deleted file mode 100644 index 6dc7eac..0000000 --- a/lib/livereload.coffee +++ /dev/null @@ -1,16 +0,0 @@ -LivereloadView = require './livereload-view' - -module.exports = - livereloadView: null - - activate: (state) -> - @livereloadView = new LivereloadView - - deactivate: -> - @livereloadView.destroy() - - serialize: -> - LivereloadView: @livereloadView.serialize() - - toggle: -> - @livereloadView.toggle() diff --git a/lib/livereload.js b/lib/livereload.js new file mode 100644 index 0000000..7adb3ad --- /dev/null +++ b/lib/livereload.js @@ -0,0 +1,68 @@ +"use babel"; + +import LivereloadView from './livereload-view'; +import {CompositeDisposable} from 'atom'; + +export default { + livereloadView: null, + + config: { + port: { + title: 'Port Number', + type: 'integer', + default: 35729 + }, + exts : { + title: 'Additional Extensions', + description: 'The server will watch these comma-separated extensions as well as the defaults.', + type: 'string', + default: 'html, css, js, png, gif, jpg, php, php5, py, rb, erb, coffee' + }, + exclusions: { + title: 'Additional Exclusions', + description: 'The server will ignore these path in addition to the defaults.', + type: 'string', + default: '.DS_Store, .gitignore, .git/, .svn/, .hg/' + }, + applyJSLive: { + title: 'Apply JavaScript Live', + type: 'boolean', + description: 'If checked, LiveReload will reload JS files in the background instead of reloading the page.', + default: false + }, + applyCSSLive: { + title: 'Apply CSS Live', + type: 'boolean', + description: 'If checked, LiveReload will reload CSS files in the background instead of refreshing the page.', + default: true + }, + useHTTPS: { + title: 'Use HTTPS Protocol', + type: 'boolean', + default: false + } + }, + + activate(state) { + this.livereloadView = new LivereloadView(); + this.livereloadView.initialize(state); + this.livereloadView.attach(); + }, + + deactivate() { + if (this.statusBarTile) { + this.statusBarTile.destory(); + } + this.statusBarTile = null; + this.livereloadView.detach(); + this.livereloadView.destroy(); + }, + + serialize() { + return { livereloadViewState: this.livereloadView.serialize() }; + }, + + consumeStatusBar(statusBar) { + this.statusBarTile = statusBar.addRightTile({item:this.livereloadView, priority:100}); + } +}; diff --git a/menus/livereload.cson b/menus/livereload.cson deleted file mode 100644 index a775a94..0000000 --- a/menus/livereload.cson +++ /dev/null @@ -1,16 +0,0 @@ -# See https://atom.io/docs/latest/creating-a-package#menus for more details -'context-menu': - '.overlayer': - 'Toggle LiveReload': 'livereload:toggle' - -'menu': [ - { - 'label': 'Packages' - 'submenu': [ - 'label': 'LiveReload' - 'submenu': [ - { 'label': 'Toggle Server', 'command': 'livereload:toggle' } - ] - ] - } -] diff --git a/menus/livereload.json b/menus/livereload.json new file mode 100644 index 0000000..0654ac3 --- /dev/null +++ b/menus/livereload.json @@ -0,0 +1,18 @@ +{ + "menu": [ + { + "label": "Packages", + "submenu": [ + { + "label": "LiveReload", + "submenu": [ + { + "label": "Toggle Server", + "command": "livereload:toggle" + } + ] + } + ] + } + ] +} diff --git a/package.json b/package.json index 386f1db..056eb47 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,36 @@ { "name": "livereload", "main": "./lib/livereload", - "version": "0.2.0", - "description": "LiveReload plugin for Atom", - "activationEvents": [ - "livereload:toggle" + "version": "0.3.0", + "description": "Livereload plugin for Atom", + "keywords": [ + "livereload", + "dev" ], - "repository": "https://github.com/taggon/livereload", + "contributors": [ + "Taegon Kim " + ], + "activationCommands": { + "atom-workspace": "livereload:toggle" + }, + "consumedServices": { + "status-bar": { + "versions": { + "^1.0.0": "consumeStatusBar" + } + } + }, + "repository": "https://github.com/atom/livereload", "license": "MIT", "engines": { - "atom": ">0.50.0" + "atom": ">=1.0.0 <2.0.0" }, - "contributors": [ - "Taegon Kim " - ], "dependencies": { - "livereload": "0.3.4" + "livereload": "^0.3.7", + "lodash": "^3.10.0" + }, + "homepage": "https://github.com/taggon/livereload", + "bugs": { + "url": "https://github.com/taggon/livereload/issues" } } diff --git a/spec/livereload-spec.coffee b/spec/livereload-spec.coffee deleted file mode 100644 index 98048a0..0000000 --- a/spec/livereload-spec.coffee +++ /dev/null @@ -1,30 +0,0 @@ -{WorkspaceView} = require 'atom' -AtomLivereload = require '../lib/livereload' - -# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. -# -# To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` -# or `fdescribe`). Remove the `f` to unfocus the block. - -describe "Livereload", -> - activationPromise = null - - beforeEach -> - atom.workspaceView = new WorkspaceView - activationPromise = atom.packages.activatePackage('livereload') - - describe "when the livereload:toggle event is triggered", -> - it "attaches and then detaches the view", -> - expect(atom.workspaceView.find('.livereload')).not.toExist() - - # This is an activation event, triggering it will cause the package to be - # activated. - atom.workspaceView.trigger 'livereload:toggle' - - waitsForPromise -> - activationPromise - - runs -> - expect(atom.workspaceView.find('.livereload')).toExist() - atom.workspaceView.trigger 'livereload:toggle' - expect(atom.workspaceView.find('.livereload')).not.toExist() diff --git a/spec/livereload-view-spec.coffee b/spec/livereload-view-spec.coffee deleted file mode 100644 index 6f2efc3..0000000 --- a/spec/livereload-view-spec.coffee +++ /dev/null @@ -1,5 +0,0 @@ -AtomLivereloadView = require '../lib/livereload-view' - -describe "LivereloadView", -> - it "has one valid test", -> - expect("life").toBe "easy" diff --git a/styles/livereload.less b/styles/livereload.less new file mode 100644 index 0000000..b2c28a4 --- /dev/null +++ b/styles/livereload.less @@ -0,0 +1,8 @@ +// The ui-variables file is provided by base themes provided by Atom. +// +// See https://github.com/atom/atom-dark-ui/blob/master/styles/ui-variables.less +// for a full listing of what's available. +@import "ui-variables"; + +.livereload { +}