Codebase list jekyll / d86c594
embed source for livereload.js Pirate Praveen 5 years ago
14 changed file(s) with 2799 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 Copyright (c) 2010-2012 Andrey Tarantsov
1
2 Permission is hereby granted, free of charge, to any person obtaining
3 a copy of this software and associated documentation files (the
4 "Software"), to deal in the Software without restriction, including
5 without limitation the rights to use, copy, modify, merge, publish,
6 distribute, sublicense, and/or sell copies of the Software, and to
7 permit persons to whom the Software is furnished to do so, subject to
8 the following conditions:
9
10 The above copyright notice and this permission notice shall be
11 included in all copies or substantial portions of the Software.
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
17 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0 LiveReload.js
1 =============
2
3 What is LiveReload?
4 -------------------
5
6 LiveReload is a tool for web developers and designers. See [livereload.com](http://livereload.com) for more info.
7
8 To use LiveReload, you need a client (this script) in your browser and a server running on your development machine.
9
10 This repository (livereload.js) implements the client side of the protocol. The client connects to a LiveReload server via web sockets and listens for incoming change notifications. When a CSS or an image file is modified, it is live-refreshed without reloading the page. When any other file is modified, the page is reloaded.
11
12 The server notifies the client whenever a change is made. Available servers are:
13
14 * [LiveReload app for Mac](http://livereload.com/)
15 * [rack-livereload](https://github.com/johnbintz/rack-livereload)
16 * [guard-livereload](https://github.com/guard/guard-livereload)
17 * [grunt-contrib-watch](https://github.com/gruntjs/grunt-contrib-watch)
18 * [python-livereload](https://github.com/lepture/python-livereload)
19 * more available on Google :-)
20 * you can even write your own; refer to the [LiveReload protocol](http://help.livereload.com/kb/ecosystem/livereload-protocol)
21
22 If you are a web developer looking to _use_ LiveReload, you should refer to your LiveReload server/app/tool's documentation, rather that this repository. **You should use the copy of livereload.js script bundled with your server**, because it's guaranteed to be compatible, and may be customized for that server.
23
24 Most LiveReload server vendors will serve livereload.js on the LiveReload port. When your server is running, you can typically access the script at `http://0.0.0.0:35729/livereload.js`.
25
26 Please read on *only* if you are:
27
28 * using a server that doesn't document the usage of livereload.js
29 * interested in hacking on livereload.js or want to understand it better
30 * developing a LiveReload server
31
32
33 What is livereload.js?
34 ----------------------
35
36 This repository contains a JavaScript file implementing the client side of the LiveReload protocol. It gets change notifications from a LiveReload server and applies them to the browser.
37
38 If you are **developing** a LiveReload server, see [dist/livereload.js](https://github.com/livereload/livereload-js/raw/master/dist/livereload.js) for the latest version built using the sources in this repository. We require LiveReload server vendors to distribute livereload.js as part of their apps or tools.
39
40 An old version of this script is also bundled with the LiveReload browser extensions, but it's not getting updated and only serves for compatibility with very old clients.
41
42 Features:
43
44 * Live CSS reloading
45 * Full page reloading
46 * Protocol, WebSocket communication
47 * CSS `@import` support
48 * Live image reloading (`<img src="..." />`, `background-image` and `border-image` properties, both inline and in stylesheets)
49 * Live, in-browser LESS.js reloading
50
51 Would love, but doesn't seem possible:
52
53 * live JS reloading
54
55
56 Installing using Bower
57 ----------------------
58
59 This script is published on Bower. (But, to reiterate: the preferred method is to avoid installing it altogether, and instead use the one bundled with your LiveReload server/app/tool.)
60
61 Installation:
62
63 bower install livereload-js --save-dev
64
65 This gives you a component containing a single script file, `dist/livereload.js`.
66
67
68 Installing using npm and Browserify
69 -----------------------------------
70
71 Including livereload.js into your Browserify bundle probably makes no sense, because livereload.js isn't something you would ship to production.
72
73 But if you insist _and_ you know what you're doing, you can install LiveReload via npm:
74
75 npm install livereload-js --save
76
77 and then add this to your bundle:
78
79 window.LiveReloadOptions = { host: 'localhost' };
80 require('livereload-js');
81
82 Note that livereload-js package uses `window` and `document` globals, so won't run under Node.js environment.
83
84 The reason you need to specify `LiveReloadOptions` is that `livereload.js` won't be able to find its `<script>` tag and would normally bail out with an error message.
85
86
87 Using livereload.js
88 -------------------
89
90 This script is meant to be included into the web pages you want to monitor, like this:
91
92 <script src="http://localhost:35729/livereload.js"></script>
93
94 LiveReload 2 server listens on port `35729` and serves livereload.js over HTTP (besides speaking the web socket protocol on the same port).
95
96 A slightly smarter way is to use the host name of the current page, assuming that it is being served from the same computer. This approach enables LiveReload when viewing the web page from other devices on the network:
97
98 ```html
99 <script>document.write('<script src="http://'
100 + location.host.split(':')[0]
101 + ':35729/livereload.js"></'
102 + 'script>')</script>
103 ```
104
105
106 However, since `location.host` is empty for `file:` URLs, we need to account for that:
107
108 ```html
109 <script>document.write('<script src="http://'
110 + (location.host || 'localhost').split(':')[0]
111 + ':35729/livereload.js"></'
112 + 'script>')</script>
113 ```
114
115
116 LiveReload.js finds a `script` tag that includes `…/livereload.js` and uses it to determine the hostname/port to connect to. It also understands some options from the query string: `host`, `port`, `snipver`, `mindelay` and `maxdelay`.
117
118 `snipver` specifies a version of the snippet, so that we can warn when the snippet needs to be updated. The currently recommended `snipver` is version 1:
119
120 ```html
121 <script>document.write('<script src="http://'
122 + (location.host || 'localhost').split(':')[0]
123 + ':35729/livereload.js?snipver=1"></'
124 + 'script>')</script>
125 ```
126
127
128 Additionally, you might want to specify `mindelay` and `maxdelay`, which is minimum and maximum reconnection delay in milliseconds (defaulting to `1000` and `60000`).
129
130 Alternatively, instead of loading livereload.js from the LiveReload server, you might want to include it from a different URL. In this case, add a `host` parameter to override the host name. For example:
131
132 ```html
133 <script src="https://github.com/livereload/livereload-js/raw/master/dist/livereload.js?host=localhost"></script>
134 ```
135
136
137 Options
138 -------
139
140 Options can either be specified as query parameters of the `<script src="..../livereload.js">` tag's source URL, or as a global `window.LiveReloadOptions` dictionary. If the dictionary is specified, `livereload.js` does not even try looking for its `<script>` tag.
141
142 The set of supported options is the same for both methods:
143
144 * `host`: the host that runs a LiveReload server; required if specifying `LiveReloadOptions`, otherwise will be autodetected as the origin of the `<script>` tag
145 * `port`: optional server port override
146 * `path`: optional path to livereload server (default: 'livereload')
147 * `mindelay`, `maxdelay`: range of reconnection delays (if `livereload.js` cannot connect to the server, it will attempt to reconnect with increasing delays); defaults to 1,000 ms minimum and 60,000 ms maximum
148 * `handshake_timeout`: timeout for a protocol handshake to be completed after a connection attempt; mostly only needed if you're running an interactive debugger on your web socket server
149 * `isChromeExtension`: reload chrome runtime instead of page when true (default: false)
150 * `reloadMissingCSS`: prevent reload of CSS when changed stylesheet isn't found in page (default: true)
151
152
153 Issues & Limitations
154 --------------------
155
156 **Serving livereload.js outside of the domain root.** Livereload.js expects to be served from the domain root (i.e. `http://myawesomeblog.com/livereload.js`). Serving from outside the domain root is possible, just add the `host` parameter to the `script` tag (see parameters documentation above).
157
158 **Live reloading of imported stylesheets has a 200ms lag.** Modifying a CSS `@import` rule to reference a not-yet-cached file causes WebKit to lose all document styles, so we have to apply a workaround that causes a lag.
159
160 Our workaround is to add a temporary `<link />` element for the imported stylesheet we're trying to reload, wait 200ms to make sure WebKit loads the new file, then remove `<link />` and recreate the `@import` rule. This prevents a flash of unstyled content. (We also wait 200 more milliseconds and recreate the `@import` rule again, in case those initial 200ms were not enough.)
161
162 **Live image reloading is limited to `<img src="..." />`, `background-image` and `border-image` styles.** Any other places where images can be mentioned?
163
164 **Live image reloading is limited to `jpg`, `jpeg`, `gif`, and `png` extensions.** Maybe need to add `svg` there? Anything else?
165
166
167 Communicating with livereload.js
168 --------------------------------
169
170 It is possible to communicate with a running LiveReload script using DOM events:
171
172 * fire `LiveReloadShutDown` event on `document` to make LiveReload disconnect and go away
173 * listen for `LiveReloadConnect` event on `document` to learn when the connection is established
174 * listen for `LiveReloadDisconnect` event on `document` to learn when the connection is interrupted (or fails to be established)
175
176 The `LiveReload` object is also exposed as `window.LiveReload`, with `LiveReload.disconnect()`, `LiveReload.connect()`, and `LiveReload.shutDown()` available. However, I'm not yet sure if I want to keep this API, so consider it non-contractual. (And please tell me if you have a use for it!)
177
178
179 Having trouble?
180 ---------------
181
182 To enable debugging output to console, append `?LR-verbose` to your URL.
183
184
185 Hacking on LiveReload.js
186 ------------------------
187
188 Requirements:
189
190 * Node.js with npm
191 * Grunt (`npm install grunt-cli`)
192
193 To install additional prerequisites:
194
195 npm install
196
197 To build:
198
199 grunt build
200
201 To run tests:
202
203 grunt
204
205 Manual testing: open files in `test/html/*` in various browsers, make some changes and make sure they are applied.
206
207 Testing the Browserify usage scenario: `grunt browserify:test`, then perform manual testing of `test/html/browserified/`.
208
209
210 Releasing a new version
211 -----------------------
212
213 1. Update the version number in `package.json`.
214
215 1. Run `rake version` to update the version numbers in all other files, using the one from `package.json`.
216
217 1. Run `grunt`.
218
219 1. Do some manual testing.
220
221 1. Tag the version in Git: `rake tag` then `git push --tags`.
222
223 1. `npm publish`
224
225
226 License
227 -------
228
229 livereload-js is available under the MIT license. See the LICENSE file for details.
230
231
232 Version history
233 ---------------
234
235 2.2.1 (Jan 17, 2015)
236
237 * npm fix: actually include `/lib` in the package
238
239 2.2.0 (Jan 16, 2015)
240
241 * the first version stitched with Browserify; everything seems to work, but 2.1.0 is available just in case
242 * switched the build system to Grunt
243
244 2.1.0 (Jan 16, 2015)
245
246 * use case-insensitive matching for `rel` attribute in `<link rel="stylesheet">` tags, to accommodate legacy Rails versions
247 * avoid usage of `console` when it's not definited
248 * some README changes
249
250 2.0.8 (May 22, 2012)
251
252 * Fix live CSS refresh to work with prefixfree
253 * Correctly trigger removal of old `<link>` tags
254
255 (older history not recorded)
0 /******/ (function(modules) { // webpackBootstrap
1 /******/ // The module cache
2 /******/ var installedModules = {};
3 /******/
4 /******/ // The require function
5 /******/ function __webpack_require__(moduleId) {
6 /******/
7 /******/ // Check if module is in cache
8 /******/ if(installedModules[moduleId]) {
9 /******/ return installedModules[moduleId].exports;
10 /******/ }
11 /******/ // Create a new module (and put it into the cache)
12 /******/ var module = installedModules[moduleId] = {
13 /******/ i: moduleId,
14 /******/ l: false,
15 /******/ exports: {}
16 /******/ };
17 /******/
18 /******/ // Execute the module function
19 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
20 /******/
21 /******/ // Flag the module as loaded
22 /******/ module.l = true;
23 /******/
24 /******/ // Return the exports of the module
25 /******/ return module.exports;
26 /******/ }
27 /******/
28 /******/
29 /******/ // expose the modules object (__webpack_modules__)
30 /******/ __webpack_require__.m = modules;
31 /******/
32 /******/ // expose the module cache
33 /******/ __webpack_require__.c = installedModules;
34 /******/
35 /******/ // define getter function for harmony exports
36 /******/ __webpack_require__.d = function(exports, name, getter) {
37 /******/ if(!__webpack_require__.o(exports, name)) {
38 /******/ Object.defineProperty(exports, name, {
39 /******/ configurable: false,
40 /******/ enumerable: true,
41 /******/ get: getter
42 /******/ });
43 /******/ }
44 /******/ };
45 /******/
46 /******/ // getDefaultExport function for compatibility with non-harmony modules
47 /******/ __webpack_require__.n = function(module) {
48 /******/ var getter = module && module.__esModule ?
49 /******/ function getDefault() { return module['default']; } :
50 /******/ function getModuleExports() { return module; };
51 /******/ __webpack_require__.d(getter, 'a', getter);
52 /******/ return getter;
53 /******/ };
54 /******/
55 /******/ // Object.prototype.hasOwnProperty.call
56 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
57 /******/
58 /******/ // __webpack_public_path__
59 /******/ __webpack_require__.p = "";
60 /******/
61 /******/ // Load entry module and return exports
62 /******/ return __webpack_require__(__webpack_require__.s = 1);
63 /******/ })
64 /************************************************************************/
65 /******/ ([
66 /* 0 */
67 /***/ (function(module, exports) {
68
69 (function() {
70 var PROTOCOL_6, PROTOCOL_7, Parser, ProtocolError,
71 __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
72
73 exports.PROTOCOL_6 = PROTOCOL_6 = 'http://livereload.com/protocols/official-6';
74
75 exports.PROTOCOL_7 = PROTOCOL_7 = 'http://livereload.com/protocols/official-7';
76
77 exports.ProtocolError = ProtocolError = (function() {
78 function ProtocolError(reason, data) {
79 this.message = "LiveReload protocol error (" + reason + ") after receiving data: \"" + data + "\".";
80 }
81
82 return ProtocolError;
83
84 })();
85
86 exports.Parser = Parser = (function() {
87 function Parser(handlers) {
88 this.handlers = handlers;
89 this.reset();
90 }
91
92 Parser.prototype.reset = function() {
93 return this.protocol = null;
94 };
95
96 Parser.prototype.process = function(data) {
97 var command, e, message, options, _ref;
98 try {
99 if (this.protocol == null) {
100 if (data.match(/^!!ver:([\d.]+)$/)) {
101 this.protocol = 6;
102 } else if (message = this._parseMessage(data, ['hello'])) {
103 if (!message.protocols.length) {
104 throw new ProtocolError("no protocols specified in handshake message");
105 } else if (__indexOf.call(message.protocols, PROTOCOL_7) >= 0) {
106 this.protocol = 7;
107 } else if (__indexOf.call(message.protocols, PROTOCOL_6) >= 0) {
108 this.protocol = 6;
109 } else {
110 throw new ProtocolError("no supported protocols found");
111 }
112 }
113 return this.handlers.connected(this.protocol);
114 } else if (this.protocol === 6) {
115 message = JSON.parse(data);
116 if (!message.length) {
117 throw new ProtocolError("protocol 6 messages must be arrays");
118 }
119 command = message[0], options = message[1];
120 if (command !== 'refresh') {
121 throw new ProtocolError("unknown protocol 6 command");
122 }
123 return this.handlers.message({
124 command: 'reload',
125 path: options.path,
126 liveCSS: (_ref = options.apply_css_live) != null ? _ref : true
127 });
128 } else {
129 message = this._parseMessage(data, ['reload', 'alert']);
130 return this.handlers.message(message);
131 }
132 } catch (_error) {
133 e = _error;
134 if (e instanceof ProtocolError) {
135 return this.handlers.error(e);
136 } else {
137 throw e;
138 }
139 }
140 };
141
142 Parser.prototype._parseMessage = function(data, validCommands) {
143 var e, message, _ref;
144 try {
145 message = JSON.parse(data);
146 } catch (_error) {
147 e = _error;
148 throw new ProtocolError('unparsable JSON', data);
149 }
150 if (!message.command) {
151 throw new ProtocolError('missing "command" key', data);
152 }
153 if (_ref = message.command, __indexOf.call(validCommands, _ref) < 0) {
154 throw new ProtocolError("invalid command '" + message.command + "', only valid commands are: " + (validCommands.join(', ')) + ")", data);
155 }
156 return message;
157 };
158
159 return Parser;
160
161 })();
162
163 }).call(this);
164
165
166 /***/ }),
167 /* 1 */
168 /***/ (function(module, exports, __webpack_require__) {
169
170 (function() {
171 var CustomEvents, LiveReload, k;
172
173 CustomEvents = __webpack_require__(2);
174
175 LiveReload = window.LiveReload = new (__webpack_require__(3).LiveReload)(window);
176
177 for (k in window) {
178 if (k.match(/^LiveReloadPlugin/)) {
179 LiveReload.addPlugin(window[k]);
180 }
181 }
182
183 LiveReload.addPlugin(__webpack_require__(8));
184
185 LiveReload.on('shutdown', function() {
186 return delete window.LiveReload;
187 });
188
189 LiveReload.on('connect', function() {
190 return CustomEvents.fire(document, 'LiveReloadConnect');
191 });
192
193 LiveReload.on('disconnect', function() {
194 return CustomEvents.fire(document, 'LiveReloadDisconnect');
195 });
196
197 CustomEvents.bind(document, 'LiveReloadShutDown', function() {
198 return LiveReload.shutDown();
199 });
200
201 }).call(this);
202
203
204 /***/ }),
205 /* 2 */
206 /***/ (function(module, exports) {
207
208 (function() {
209 var CustomEvents;
210
211 CustomEvents = {
212 bind: function(element, eventName, handler) {
213 if (element.addEventListener) {
214 return element.addEventListener(eventName, handler, false);
215 } else if (element.attachEvent) {
216 element[eventName] = 1;
217 return element.attachEvent('onpropertychange', function(event) {
218 if (event.propertyName === eventName) {
219 return handler();
220 }
221 });
222 } else {
223 throw new Error("Attempt to attach custom event " + eventName + " to something which isn't a DOMElement");
224 }
225 },
226 fire: function(element, eventName) {
227 var event;
228 if (element.addEventListener) {
229 event = document.createEvent('HTMLEvents');
230 event.initEvent(eventName, true, true);
231 return document.dispatchEvent(event);
232 } else if (element.attachEvent) {
233 if (element[eventName]) {
234 return element[eventName]++;
235 }
236 } else {
237 throw new Error("Attempt to fire custom event " + eventName + " on something which isn't a DOMElement");
238 }
239 }
240 };
241
242 exports.bind = CustomEvents.bind;
243
244 exports.fire = CustomEvents.fire;
245
246 }).call(this);
247
248
249 /***/ }),
250 /* 3 */
251 /***/ (function(module, exports, __webpack_require__) {
252
253 (function() {
254 var Connector, LiveReload, Options, ProtocolError, Reloader, Timer,
255 __hasProp = {}.hasOwnProperty;
256
257 Connector = __webpack_require__(4).Connector;
258
259 Timer = __webpack_require__(5).Timer;
260
261 Options = __webpack_require__(6).Options;
262
263 Reloader = __webpack_require__(7).Reloader;
264
265 ProtocolError = __webpack_require__(0).ProtocolError;
266
267 exports.LiveReload = LiveReload = (function() {
268 function LiveReload(window) {
269 var k, v, _ref;
270 this.window = window;
271 this.listeners = {};
272 this.plugins = [];
273 this.pluginIdentifiers = {};
274 this.console = this.window.console && this.window.console.log && this.window.console.error ? this.window.location.href.match(/LR-verbose/) ? this.window.console : {
275 log: function() {},
276 error: this.window.console.error.bind(this.window.console)
277 } : {
278 log: function() {},
279 error: function() {}
280 };
281 if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) {
282 this.console.error("LiveReload disabled because the browser does not seem to support web sockets");
283 return;
284 }
285 if ('LiveReloadOptions' in window) {
286 this.options = new Options();
287 _ref = window['LiveReloadOptions'];
288 for (k in _ref) {
289 if (!__hasProp.call(_ref, k)) continue;
290 v = _ref[k];
291 this.options.set(k, v);
292 }
293 } else {
294 this.options = Options.extract(this.window.document);
295 if (!this.options) {
296 this.console.error("LiveReload disabled because it could not find its own <SCRIPT> tag");
297 return;
298 }
299 }
300 this.reloader = new Reloader(this.window, this.console, Timer);
301 this.connector = new Connector(this.options, this.WebSocket, Timer, {
302 connecting: (function(_this) {
303 return function() {};
304 })(this),
305 socketConnected: (function(_this) {
306 return function() {};
307 })(this),
308 connected: (function(_this) {
309 return function(protocol) {
310 var _base;
311 if (typeof (_base = _this.listeners).connect === "function") {
312 _base.connect();
313 }
314 _this.log("LiveReload is connected to " + _this.options.host + ":" + _this.options.port + " (protocol v" + protocol + ").");
315 return _this.analyze();
316 };
317 })(this),
318 error: (function(_this) {
319 return function(e) {
320 if (e instanceof ProtocolError) {
321 if (typeof console !== "undefined" && console !== null) {
322 return console.log("" + e.message + ".");
323 }
324 } else {
325 if (typeof console !== "undefined" && console !== null) {
326 return console.log("LiveReload internal error: " + e.message);
327 }
328 }
329 };
330 })(this),
331 disconnected: (function(_this) {
332 return function(reason, nextDelay) {
333 var _base;
334 if (typeof (_base = _this.listeners).disconnect === "function") {
335 _base.disconnect();
336 }
337 switch (reason) {
338 case 'cannot-connect':
339 return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + ", will retry in " + nextDelay + " sec.");
340 case 'broken':
341 return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + ", reconnecting in " + nextDelay + " sec.");
342 case 'handshake-timeout':
343 return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake timeout), will retry in " + nextDelay + " sec.");
344 case 'handshake-failed':
345 return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake failed), will retry in " + nextDelay + " sec.");
346 case 'manual':
347 break;
348 case 'error':
349 break;
350 default:
351 return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + " (" + reason + "), reconnecting in " + nextDelay + " sec.");
352 }
353 };
354 })(this),
355 message: (function(_this) {
356 return function(message) {
357 switch (message.command) {
358 case 'reload':
359 return _this.performReload(message);
360 case 'alert':
361 return _this.performAlert(message);
362 }
363 };
364 })(this)
365 });
366 this.initialized = true;
367 }
368
369 LiveReload.prototype.on = function(eventName, handler) {
370 return this.listeners[eventName] = handler;
371 };
372
373 LiveReload.prototype.log = function(message) {
374 return this.console.log("" + message);
375 };
376
377 LiveReload.prototype.performReload = function(message) {
378 var _ref, _ref1, _ref2;
379 this.log("LiveReload received reload request: " + (JSON.stringify(message, null, 2)));
380 return this.reloader.reload(message.path, {
381 liveCSS: (_ref = message.liveCSS) != null ? _ref : true,
382 liveImg: (_ref1 = message.liveImg) != null ? _ref1 : true,
383 reloadMissingCSS: (_ref2 = message.reloadMissingCSS) != null ? _ref2 : true,
384 originalPath: message.originalPath || '',
385 overrideURL: message.overrideURL || '',
386 serverURL: "http://" + this.options.host + ":" + this.options.port
387 });
388 };
389
390 LiveReload.prototype.performAlert = function(message) {
391 return alert(message.message);
392 };
393
394 LiveReload.prototype.shutDown = function() {
395 var _base;
396 if (!this.initialized) {
397 return;
398 }
399 this.connector.disconnect();
400 this.log("LiveReload disconnected.");
401 return typeof (_base = this.listeners).shutdown === "function" ? _base.shutdown() : void 0;
402 };
403
404 LiveReload.prototype.hasPlugin = function(identifier) {
405 return !!this.pluginIdentifiers[identifier];
406 };
407
408 LiveReload.prototype.addPlugin = function(pluginClass) {
409 var plugin;
410 if (!this.initialized) {
411 return;
412 }
413 if (this.hasPlugin(pluginClass.identifier)) {
414 return;
415 }
416 this.pluginIdentifiers[pluginClass.identifier] = true;
417 plugin = new pluginClass(this.window, {
418 _livereload: this,
419 _reloader: this.reloader,
420 _connector: this.connector,
421 console: this.console,
422 Timer: Timer,
423 generateCacheBustUrl: (function(_this) {
424 return function(url) {
425 return _this.reloader.generateCacheBustUrl(url);
426 };
427 })(this)
428 });
429 this.plugins.push(plugin);
430 this.reloader.addPlugin(plugin);
431 };
432
433 LiveReload.prototype.analyze = function() {
434 var plugin, pluginData, pluginsData, _i, _len, _ref;
435 if (!this.initialized) {
436 return;
437 }
438 if (!(this.connector.protocol >= 7)) {
439 return;
440 }
441 pluginsData = {};
442 _ref = this.plugins;
443 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
444 plugin = _ref[_i];
445 pluginsData[plugin.constructor.identifier] = pluginData = (typeof plugin.analyze === "function" ? plugin.analyze() : void 0) || {};
446 pluginData.version = plugin.constructor.version;
447 }
448 this.connector.sendCommand({
449 command: 'info',
450 plugins: pluginsData,
451 url: this.window.location.href
452 });
453 };
454
455 return LiveReload;
456
457 })();
458
459 }).call(this);
460
461
462 /***/ }),
463 /* 4 */
464 /***/ (function(module, exports, __webpack_require__) {
465
466 (function() {
467 var Connector, PROTOCOL_6, PROTOCOL_7, Parser, Version, _ref;
468
469 _ref = __webpack_require__(0), Parser = _ref.Parser, PROTOCOL_6 = _ref.PROTOCOL_6, PROTOCOL_7 = _ref.PROTOCOL_7;
470
471 Version = '2.2.2';
472
473 exports.Connector = Connector = (function() {
474 function Connector(options, WebSocket, Timer, handlers) {
475 var path;
476 this.options = options;
477 this.WebSocket = WebSocket;
478 this.Timer = Timer;
479 this.handlers = handlers;
480 path = this.options.path ? "" + this.options.path : "livereload";
481 this._uri = "ws" + (this.options.https ? "s" : "") + "://" + this.options.host + ":" + this.options.port + "/" + path;
482 this._nextDelay = this.options.mindelay;
483 this._connectionDesired = false;
484 this.protocol = 0;
485 this.protocolParser = new Parser({
486 connected: (function(_this) {
487 return function(protocol) {
488 _this.protocol = protocol;
489 _this._handshakeTimeout.stop();
490 _this._nextDelay = _this.options.mindelay;
491 _this._disconnectionReason = 'broken';
492 return _this.handlers.connected(protocol);
493 };
494 })(this),
495 error: (function(_this) {
496 return function(e) {
497 _this.handlers.error(e);
498 return _this._closeOnError();
499 };
500 })(this),
501 message: (function(_this) {
502 return function(message) {
503 return _this.handlers.message(message);
504 };
505 })(this)
506 });
507 this._handshakeTimeout = new Timer((function(_this) {
508 return function() {
509 if (!_this._isSocketConnected()) {
510 return;
511 }
512 _this._disconnectionReason = 'handshake-timeout';
513 return _this.socket.close();
514 };
515 })(this));
516 this._reconnectTimer = new Timer((function(_this) {
517 return function() {
518 if (!_this._connectionDesired) {
519 return;
520 }
521 return _this.connect();
522 };
523 })(this));
524 this.connect();
525 }
526
527 Connector.prototype._isSocketConnected = function() {
528 return this.socket && this.socket.readyState === this.WebSocket.OPEN;
529 };
530
531 Connector.prototype.connect = function() {
532 this._connectionDesired = true;
533 if (this._isSocketConnected()) {
534 return;
535 }
536 this._reconnectTimer.stop();
537 this._disconnectionReason = 'cannot-connect';
538 this.protocolParser.reset();
539 this.handlers.connecting();
540 this.socket = new this.WebSocket(this._uri);
541 this.socket.onopen = (function(_this) {
542 return function(e) {
543 return _this._onopen(e);
544 };
545 })(this);
546 this.socket.onclose = (function(_this) {
547 return function(e) {
548 return _this._onclose(e);
549 };
550 })(this);
551 this.socket.onmessage = (function(_this) {
552 return function(e) {
553 return _this._onmessage(e);
554 };
555 })(this);
556 return this.socket.onerror = (function(_this) {
557 return function(e) {
558 return _this._onerror(e);
559 };
560 })(this);
561 };
562
563 Connector.prototype.disconnect = function() {
564 this._connectionDesired = false;
565 this._reconnectTimer.stop();
566 if (!this._isSocketConnected()) {
567 return;
568 }
569 this._disconnectionReason = 'manual';
570 return this.socket.close();
571 };
572
573 Connector.prototype._scheduleReconnection = function() {
574 if (!this._connectionDesired) {
575 return;
576 }
577 if (!this._reconnectTimer.running) {
578 this._reconnectTimer.start(this._nextDelay);
579 return this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2);
580 }
581 };
582
583 Connector.prototype.sendCommand = function(command) {
584 if (this.protocol == null) {
585 return;
586 }
587 return this._sendCommand(command);
588 };
589
590 Connector.prototype._sendCommand = function(command) {
591 return this.socket.send(JSON.stringify(command));
592 };
593
594 Connector.prototype._closeOnError = function() {
595 this._handshakeTimeout.stop();
596 this._disconnectionReason = 'error';
597 return this.socket.close();
598 };
599
600 Connector.prototype._onopen = function(e) {
601 var hello;
602 this.handlers.socketConnected();
603 this._disconnectionReason = 'handshake-failed';
604 hello = {
605 command: 'hello',
606 protocols: [PROTOCOL_6, PROTOCOL_7]
607 };
608 hello.ver = Version;
609 if (this.options.ext) {
610 hello.ext = this.options.ext;
611 }
612 if (this.options.extver) {
613 hello.extver = this.options.extver;
614 }
615 if (this.options.snipver) {
616 hello.snipver = this.options.snipver;
617 }
618 this._sendCommand(hello);
619 return this._handshakeTimeout.start(this.options.handshake_timeout);
620 };
621
622 Connector.prototype._onclose = function(e) {
623 this.protocol = 0;
624 this.handlers.disconnected(this._disconnectionReason, this._nextDelay);
625 return this._scheduleReconnection();
626 };
627
628 Connector.prototype._onerror = function(e) {};
629
630 Connector.prototype._onmessage = function(e) {
631 return this.protocolParser.process(e.data);
632 };
633
634 return Connector;
635
636 })();
637
638 }).call(this);
639
640
641 /***/ }),
642 /* 5 */
643 /***/ (function(module, exports) {
644
645 (function() {
646 var Timer;
647
648 exports.Timer = Timer = (function() {
649 function Timer(func) {
650 this.func = func;
651 this.running = false;
652 this.id = null;
653 this._handler = (function(_this) {
654 return function() {
655 _this.running = false;
656 _this.id = null;
657 return _this.func();
658 };
659 })(this);
660 }
661
662 Timer.prototype.start = function(timeout) {
663 if (this.running) {
664 clearTimeout(this.id);
665 }
666 this.id = setTimeout(this._handler, timeout);
667 return this.running = true;
668 };
669
670 Timer.prototype.stop = function() {
671 if (this.running) {
672 clearTimeout(this.id);
673 this.running = false;
674 return this.id = null;
675 }
676 };
677
678 return Timer;
679
680 })();
681
682 Timer.start = function(timeout, func) {
683 return setTimeout(func, timeout);
684 };
685
686 }).call(this);
687
688
689 /***/ }),
690 /* 6 */
691 /***/ (function(module, exports) {
692
693 (function() {
694 var Options;
695
696 exports.Options = Options = (function() {
697 function Options() {
698 this.https = false;
699 this.host = null;
700 this.port = 35729;
701 this.snipver = null;
702 this.ext = null;
703 this.extver = null;
704 this.mindelay = 1000;
705 this.maxdelay = 60000;
706 this.handshake_timeout = 5000;
707 }
708
709 Options.prototype.set = function(name, value) {
710 if (typeof value === 'undefined') {
711 return;
712 }
713 if (!isNaN(+value)) {
714 value = +value;
715 }
716 return this[name] = value;
717 };
718
719 return Options;
720
721 })();
722
723 Options.extract = function(document) {
724 var element, keyAndValue, m, mm, options, pair, src, _i, _j, _len, _len1, _ref, _ref1;
725 _ref = document.getElementsByTagName('script');
726 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
727 element = _ref[_i];
728 if ((src = element.src) && (m = src.match(/^[^:]+:\/\/(.*)\/z?livereload\.js(?:\?(.*))?$/))) {
729 options = new Options();
730 options.https = src.indexOf("https") === 0;
731 if (mm = m[1].match(/^([^\/:]+)(?::(\d+))?$/)) {
732 options.host = mm[1];
733 if (mm[2]) {
734 options.port = parseInt(mm[2], 10);
735 }
736 }
737 if (m[2]) {
738 _ref1 = m[2].split('&');
739 for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
740 pair = _ref1[_j];
741 if ((keyAndValue = pair.split('=')).length > 1) {
742 options.set(keyAndValue[0].replace(/-/g, '_'), keyAndValue.slice(1).join('='));
743 }
744 }
745 }
746 return options;
747 }
748 }
749 return null;
750 };
751
752 }).call(this);
753
754
755 /***/ }),
756 /* 7 */
757 /***/ (function(module, exports) {
758
759 (function() {
760 var IMAGE_STYLES, Reloader, numberOfMatchingSegments, pathFromUrl, pathsMatch, pickBestMatch, splitUrl;
761
762 splitUrl = function(url) {
763 var comboSign, hash, index, params;
764 if ((index = url.indexOf('#')) >= 0) {
765 hash = url.slice(index);
766 url = url.slice(0, index);
767 } else {
768 hash = '';
769 }
770 comboSign = url.indexOf('??');
771 if (comboSign >= 0) {
772 if (comboSign + 1 !== url.lastIndexOf('?')) {
773 index = url.lastIndexOf('?');
774 }
775 } else {
776 index = url.indexOf('?');
777 }
778 if (index >= 0) {
779 params = url.slice(index);
780 url = url.slice(0, index);
781 } else {
782 params = '';
783 }
784 return {
785 url: url,
786 params: params,
787 hash: hash
788 };
789 };
790
791 pathFromUrl = function(url) {
792 var path;
793 url = splitUrl(url).url;
794 if (url.indexOf('file://') === 0) {
795 path = url.replace(/^file:\/\/(localhost)?/, '');
796 } else {
797 path = url.replace(/^([^:]+:)?\/\/([^:\/]+)(:\d*)?\//, '/');
798 }
799 return decodeURIComponent(path);
800 };
801
802 pickBestMatch = function(path, objects, pathFunc) {
803 var bestMatch, object, score, _i, _len;
804 bestMatch = {
805 score: 0
806 };
807 for (_i = 0, _len = objects.length; _i < _len; _i++) {
808 object = objects[_i];
809 score = numberOfMatchingSegments(path, pathFunc(object));
810 if (score > bestMatch.score) {
811 bestMatch = {
812 object: object,
813 score: score
814 };
815 }
816 }
817 if (bestMatch.score > 0) {
818 return bestMatch;
819 } else {
820 return null;
821 }
822 };
823
824 numberOfMatchingSegments = function(path1, path2) {
825 var comps1, comps2, eqCount, len;
826 path1 = path1.replace(/^\/+/, '').toLowerCase();
827 path2 = path2.replace(/^\/+/, '').toLowerCase();
828 if (path1 === path2) {
829 return 10000;
830 }
831 comps1 = path1.split('/').reverse();
832 comps2 = path2.split('/').reverse();
833 len = Math.min(comps1.length, comps2.length);
834 eqCount = 0;
835 while (eqCount < len && comps1[eqCount] === comps2[eqCount]) {
836 ++eqCount;
837 }
838 return eqCount;
839 };
840
841 pathsMatch = function(path1, path2) {
842 return numberOfMatchingSegments(path1, path2) > 0;
843 };
844
845 IMAGE_STYLES = [
846 {
847 selector: 'background',
848 styleNames: ['backgroundImage']
849 }, {
850 selector: 'border',
851 styleNames: ['borderImage', 'webkitBorderImage', 'MozBorderImage']
852 }
853 ];
854
855 exports.Reloader = Reloader = (function() {
856 function Reloader(window, console, Timer) {
857 this.window = window;
858 this.console = console;
859 this.Timer = Timer;
860 this.document = this.window.document;
861 this.importCacheWaitPeriod = 200;
862 this.plugins = [];
863 }
864
865 Reloader.prototype.addPlugin = function(plugin) {
866 return this.plugins.push(plugin);
867 };
868
869 Reloader.prototype.analyze = function(callback) {
870 return results;
871 };
872
873 Reloader.prototype.reload = function(path, options) {
874 var plugin, _base, _i, _len, _ref;
875 this.options = options;
876 if ((_base = this.options).stylesheetReloadTimeout == null) {
877 _base.stylesheetReloadTimeout = 15000;
878 }
879 _ref = this.plugins;
880 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
881 plugin = _ref[_i];
882 if (plugin.reload && plugin.reload(path, options)) {
883 return;
884 }
885 }
886 if (options.liveCSS && path.match(/\.css(?:\.map)?$/i)) {
887 if (this.reloadStylesheet(path)) {
888 return;
889 }
890 }
891 if (options.liveImg && path.match(/\.(jpe?g|png|gif)$/i)) {
892 this.reloadImages(path);
893 return;
894 }
895 if (options.isChromeExtension) {
896 this.reloadChromeExtension();
897 return;
898 }
899 return this.reloadPage();
900 };
901
902 Reloader.prototype.reloadPage = function() {
903 return this.window.document.location.reload();
904 };
905
906 Reloader.prototype.reloadChromeExtension = function() {
907 return this.window.chrome.runtime.reload();
908 };
909
910 Reloader.prototype.reloadImages = function(path) {
911 var expando, img, selector, styleNames, styleSheet, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _results;
912 expando = this.generateUniqueString();
913 _ref = this.document.images;
914 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
915 img = _ref[_i];
916 if (pathsMatch(path, pathFromUrl(img.src))) {
917 img.src = this.generateCacheBustUrl(img.src, expando);
918 }
919 }
920 if (this.document.querySelectorAll) {
921 for (_j = 0, _len1 = IMAGE_STYLES.length; _j < _len1; _j++) {
922 _ref1 = IMAGE_STYLES[_j], selector = _ref1.selector, styleNames = _ref1.styleNames;
923 _ref2 = this.document.querySelectorAll("[style*=" + selector + "]");
924 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
925 img = _ref2[_k];
926 this.reloadStyleImages(img.style, styleNames, path, expando);
927 }
928 }
929 }
930 if (this.document.styleSheets) {
931 _ref3 = this.document.styleSheets;
932 _results = [];
933 for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
934 styleSheet = _ref3[_l];
935 _results.push(this.reloadStylesheetImages(styleSheet, path, expando));
936 }
937 return _results;
938 }
939 };
940
941 Reloader.prototype.reloadStylesheetImages = function(styleSheet, path, expando) {
942 var e, rule, rules, styleNames, _i, _j, _len, _len1;
943 try {
944 rules = styleSheet != null ? styleSheet.cssRules : void 0;
945 } catch (_error) {
946 e = _error;
947 }
948 if (!rules) {
949 return;
950 }
951 for (_i = 0, _len = rules.length; _i < _len; _i++) {
952 rule = rules[_i];
953 switch (rule.type) {
954 case CSSRule.IMPORT_RULE:
955 this.reloadStylesheetImages(rule.styleSheet, path, expando);
956 break;
957 case CSSRule.STYLE_RULE:
958 for (_j = 0, _len1 = IMAGE_STYLES.length; _j < _len1; _j++) {
959 styleNames = IMAGE_STYLES[_j].styleNames;
960 this.reloadStyleImages(rule.style, styleNames, path, expando);
961 }
962 break;
963 case CSSRule.MEDIA_RULE:
964 this.reloadStylesheetImages(rule, path, expando);
965 }
966 }
967 };
968
969 Reloader.prototype.reloadStyleImages = function(style, styleNames, path, expando) {
970 var newValue, styleName, value, _i, _len;
971 for (_i = 0, _len = styleNames.length; _i < _len; _i++) {
972 styleName = styleNames[_i];
973 value = style[styleName];
974 if (typeof value === 'string') {
975 newValue = value.replace(/\burl\s*\(([^)]*)\)/, (function(_this) {
976 return function(match, src) {
977 if (pathsMatch(path, pathFromUrl(src))) {
978 return "url(" + (_this.generateCacheBustUrl(src, expando)) + ")";
979 } else {
980 return match;
981 }
982 };
983 })(this));
984 if (newValue !== value) {
985 style[styleName] = newValue;
986 }
987 }
988 }
989 };
990
991 Reloader.prototype.reloadStylesheet = function(path) {
992 var imported, link, links, match, style, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1;
993 links = (function() {
994 var _i, _len, _ref, _results;
995 _ref = this.document.getElementsByTagName('link');
996 _results = [];
997 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
998 link = _ref[_i];
999 if (link.rel.match(/^stylesheet$/i) && !link.__LiveReload_pendingRemoval) {
1000 _results.push(link);
1001 }
1002 }
1003 return _results;
1004 }).call(this);
1005 imported = [];
1006 _ref = this.document.getElementsByTagName('style');
1007 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1008 style = _ref[_i];
1009 if (style.sheet) {
1010 this.collectImportedStylesheets(style, style.sheet, imported);
1011 }
1012 }
1013 for (_j = 0, _len1 = links.length; _j < _len1; _j++) {
1014 link = links[_j];
1015 this.collectImportedStylesheets(link, link.sheet, imported);
1016 }
1017 if (this.window.StyleFix && this.document.querySelectorAll) {
1018 _ref1 = this.document.querySelectorAll('style[data-href]');
1019 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
1020 style = _ref1[_k];
1021 links.push(style);
1022 }
1023 }
1024 this.console.log("LiveReload found " + links.length + " LINKed stylesheets, " + imported.length + " @imported stylesheets");
1025 match = pickBestMatch(path, links.concat(imported), (function(_this) {
1026 return function(l) {
1027 return pathFromUrl(_this.linkHref(l));
1028 };
1029 })(this));
1030 if (match) {
1031 if (match.object.rule) {
1032 this.console.log("LiveReload is reloading imported stylesheet: " + match.object.href);
1033 this.reattachImportedRule(match.object);
1034 } else {
1035 this.console.log("LiveReload is reloading stylesheet: " + (this.linkHref(match.object)));
1036 this.reattachStylesheetLink(match.object);
1037 }
1038 } else {
1039 if (this.options.reloadMissingCSS) {
1040 this.console.log("LiveReload will reload all stylesheets because path '" + path + "' did not match any specific one. To disable this behavior, set 'options.reloadMissingCSS' to 'false'.");
1041 for (_l = 0, _len3 = links.length; _l < _len3; _l++) {
1042 link = links[_l];
1043 this.reattachStylesheetLink(link);
1044 }
1045 } else {
1046 this.console.log("LiveReload will not reload path '" + path + "' because the stylesheet was not found on the page and 'options.reloadMissingCSS' was set to 'false'.");
1047 }
1048 }
1049 return true;
1050 };
1051
1052 Reloader.prototype.collectImportedStylesheets = function(link, styleSheet, result) {
1053 var e, index, rule, rules, _i, _len;
1054 try {
1055 rules = styleSheet != null ? styleSheet.cssRules : void 0;
1056 } catch (_error) {
1057 e = _error;
1058 }
1059 if (rules && rules.length) {
1060 for (index = _i = 0, _len = rules.length; _i < _len; index = ++_i) {
1061 rule = rules[index];
1062 switch (rule.type) {
1063 case CSSRule.CHARSET_RULE:
1064 continue;
1065 case CSSRule.IMPORT_RULE:
1066 result.push({
1067 link: link,
1068 rule: rule,
1069 index: index,
1070 href: rule.href
1071 });
1072 this.collectImportedStylesheets(link, rule.styleSheet, result);
1073 break;
1074 default:
1075 break;
1076 }
1077 }
1078 }
1079 };
1080
1081 Reloader.prototype.waitUntilCssLoads = function(clone, func) {
1082 var callbackExecuted, executeCallback, poll;
1083 callbackExecuted = false;
1084 executeCallback = (function(_this) {
1085 return function() {
1086 if (callbackExecuted) {
1087 return;
1088 }
1089 callbackExecuted = true;
1090 return func();
1091 };
1092 })(this);
1093 clone.onload = (function(_this) {
1094 return function() {
1095 _this.console.log("LiveReload: the new stylesheet has finished loading");
1096 _this.knownToSupportCssOnLoad = true;
1097 return executeCallback();
1098 };
1099 })(this);
1100 if (!this.knownToSupportCssOnLoad) {
1101 (poll = (function(_this) {
1102 return function() {
1103 if (clone.sheet) {
1104 _this.console.log("LiveReload is polling until the new CSS finishes loading...");
1105 return executeCallback();
1106 } else {
1107 return _this.Timer.start(50, poll);
1108 }
1109 };
1110 })(this))();
1111 }
1112 return this.Timer.start(this.options.stylesheetReloadTimeout, executeCallback);
1113 };
1114
1115 Reloader.prototype.linkHref = function(link) {
1116 return link.href || link.getAttribute('data-href');
1117 };
1118
1119 Reloader.prototype.reattachStylesheetLink = function(link) {
1120 var clone, parent;
1121 if (link.__LiveReload_pendingRemoval) {
1122 return;
1123 }
1124 link.__LiveReload_pendingRemoval = true;
1125 if (link.tagName === 'STYLE') {
1126 clone = this.document.createElement('link');
1127 clone.rel = 'stylesheet';
1128 clone.media = link.media;
1129 clone.disabled = link.disabled;
1130 } else {
1131 clone = link.cloneNode(false);
1132 }
1133 clone.href = this.generateCacheBustUrl(this.linkHref(link));
1134 parent = link.parentNode;
1135 if (parent.lastChild === link) {
1136 parent.appendChild(clone);
1137 } else {
1138 parent.insertBefore(clone, link.nextSibling);
1139 }
1140 return this.waitUntilCssLoads(clone, (function(_this) {
1141 return function() {
1142 var additionalWaitingTime;
1143 if (/AppleWebKit/.test(navigator.userAgent)) {
1144 additionalWaitingTime = 5;
1145 } else {
1146 additionalWaitingTime = 200;
1147 }
1148 return _this.Timer.start(additionalWaitingTime, function() {
1149 var _ref;
1150 if (!link.parentNode) {
1151 return;
1152 }
1153 link.parentNode.removeChild(link);
1154 clone.onreadystatechange = null;
1155 return (_ref = _this.window.StyleFix) != null ? _ref.link(clone) : void 0;
1156 });
1157 };
1158 })(this));
1159 };
1160
1161 Reloader.prototype.reattachImportedRule = function(_arg) {
1162 var href, index, link, media, newRule, parent, rule, tempLink;
1163 rule = _arg.rule, index = _arg.index, link = _arg.link;
1164 parent = rule.parentStyleSheet;
1165 href = this.generateCacheBustUrl(rule.href);
1166 media = rule.media.length ? [].join.call(rule.media, ', ') : '';
1167 newRule = "@import url(\"" + href + "\") " + media + ";";
1168 rule.__LiveReload_newHref = href;
1169 tempLink = this.document.createElement("link");
1170 tempLink.rel = 'stylesheet';
1171 tempLink.href = href;
1172 tempLink.__LiveReload_pendingRemoval = true;
1173 if (link.parentNode) {
1174 link.parentNode.insertBefore(tempLink, link);
1175 }
1176 return this.Timer.start(this.importCacheWaitPeriod, (function(_this) {
1177 return function() {
1178 if (tempLink.parentNode) {
1179 tempLink.parentNode.removeChild(tempLink);
1180 }
1181 if (rule.__LiveReload_newHref !== href) {
1182 return;
1183 }
1184 parent.insertRule(newRule, index);
1185 parent.deleteRule(index + 1);
1186 rule = parent.cssRules[index];
1187 rule.__LiveReload_newHref = href;
1188 return _this.Timer.start(_this.importCacheWaitPeriod, function() {
1189 if (rule.__LiveReload_newHref !== href) {
1190 return;
1191 }
1192 parent.insertRule(newRule, index);
1193 return parent.deleteRule(index + 1);
1194 });
1195 };
1196 })(this));
1197 };
1198
1199 Reloader.prototype.generateUniqueString = function() {
1200 return 'livereload=' + Date.now();
1201 };
1202
1203 Reloader.prototype.generateCacheBustUrl = function(url, expando) {
1204 var hash, oldParams, originalUrl, params, _ref;
1205 if (expando == null) {
1206 expando = this.generateUniqueString();
1207 }
1208 _ref = splitUrl(url), url = _ref.url, hash = _ref.hash, oldParams = _ref.params;
1209 if (this.options.overrideURL) {
1210 if (url.indexOf(this.options.serverURL) < 0) {
1211 originalUrl = url;
1212 url = this.options.serverURL + this.options.overrideURL + "?url=" + encodeURIComponent(url);
1213 this.console.log("LiveReload is overriding source URL " + originalUrl + " with " + url);
1214 }
1215 }
1216 params = oldParams.replace(/(\?|&)livereload=(\d+)/, function(match, sep) {
1217 return "" + sep + expando;
1218 });
1219 if (params === oldParams) {
1220 if (oldParams.length === 0) {
1221 params = "?" + expando;
1222 } else {
1223 params = "" + oldParams + "&" + expando;
1224 }
1225 }
1226 return url + params + hash;
1227 };
1228
1229 return Reloader;
1230
1231 })();
1232
1233 }).call(this);
1234
1235
1236 /***/ }),
1237 /* 8 */
1238 /***/ (function(module, exports) {
1239
1240 (function() {
1241 var LessPlugin;
1242
1243 module.exports = LessPlugin = (function() {
1244 LessPlugin.identifier = 'less';
1245
1246 LessPlugin.version = '1.0';
1247
1248 function LessPlugin(window, host) {
1249 this.window = window;
1250 this.host = host;
1251 }
1252
1253 LessPlugin.prototype.reload = function(path, options) {
1254 if (this.window.less && this.window.less.refresh) {
1255 if (path.match(/\.less$/i)) {
1256 return this.reloadLess(path);
1257 }
1258 if (options.originalPath.match(/\.less$/i)) {
1259 return this.reloadLess(options.originalPath);
1260 }
1261 }
1262 return false;
1263 };
1264
1265 LessPlugin.prototype.reloadLess = function(path) {
1266 var link, links, _i, _len;
1267 links = (function() {
1268 var _i, _len, _ref, _results;
1269 _ref = document.getElementsByTagName('link');
1270 _results = [];
1271 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1272 link = _ref[_i];
1273 if (link.href && link.rel.match(/^stylesheet\/less$/i) || (link.rel.match(/stylesheet/i) && link.type.match(/^text\/(x-)?less$/i))) {
1274 _results.push(link);
1275 }
1276 }
1277 return _results;
1278 })();
1279 if (links.length === 0) {
1280 return false;
1281 }
1282 for (_i = 0, _len = links.length; _i < _len; _i++) {
1283 link = links[_i];
1284 link.href = this.host.generateCacheBustUrl(link.href);
1285 }
1286 this.host.console.log("LiveReload is asking LESS to recompile all stylesheets");
1287 this.window.less.refresh(true);
1288 return true;
1289 };
1290
1291 LessPlugin.prototype.analyze = function() {
1292 return {
1293 disable: !!(this.window.less && this.window.less.refresh)
1294 };
1295 };
1296
1297 return LessPlugin;
1298
1299 })();
1300
1301 }).call(this);
1302
1303
1304 /***/ })
1305 /******/ ]);
0 (function() {
1 var Connector, PROTOCOL_6, PROTOCOL_7, Parser, Version, _ref;
2
3 _ref = require('./protocol'), Parser = _ref.Parser, PROTOCOL_6 = _ref.PROTOCOL_6, PROTOCOL_7 = _ref.PROTOCOL_7;
4
5 Version = '2.2.2';
6
7 exports.Connector = Connector = (function() {
8 function Connector(options, WebSocket, Timer, handlers) {
9 var path;
10 this.options = options;
11 this.WebSocket = WebSocket;
12 this.Timer = Timer;
13 this.handlers = handlers;
14 path = this.options.path ? "" + this.options.path : "livereload";
15 this._uri = "ws" + (this.options.https ? "s" : "") + "://" + this.options.host + ":" + this.options.port + "/" + path;
16 this._nextDelay = this.options.mindelay;
17 this._connectionDesired = false;
18 this.protocol = 0;
19 this.protocolParser = new Parser({
20 connected: (function(_this) {
21 return function(protocol) {
22 _this.protocol = protocol;
23 _this._handshakeTimeout.stop();
24 _this._nextDelay = _this.options.mindelay;
25 _this._disconnectionReason = 'broken';
26 return _this.handlers.connected(protocol);
27 };
28 })(this),
29 error: (function(_this) {
30 return function(e) {
31 _this.handlers.error(e);
32 return _this._closeOnError();
33 };
34 })(this),
35 message: (function(_this) {
36 return function(message) {
37 return _this.handlers.message(message);
38 };
39 })(this)
40 });
41 this._handshakeTimeout = new Timer((function(_this) {
42 return function() {
43 if (!_this._isSocketConnected()) {
44 return;
45 }
46 _this._disconnectionReason = 'handshake-timeout';
47 return _this.socket.close();
48 };
49 })(this));
50 this._reconnectTimer = new Timer((function(_this) {
51 return function() {
52 if (!_this._connectionDesired) {
53 return;
54 }
55 return _this.connect();
56 };
57 })(this));
58 this.connect();
59 }
60
61 Connector.prototype._isSocketConnected = function() {
62 return this.socket && this.socket.readyState === this.WebSocket.OPEN;
63 };
64
65 Connector.prototype.connect = function() {
66 this._connectionDesired = true;
67 if (this._isSocketConnected()) {
68 return;
69 }
70 this._reconnectTimer.stop();
71 this._disconnectionReason = 'cannot-connect';
72 this.protocolParser.reset();
73 this.handlers.connecting();
74 this.socket = new this.WebSocket(this._uri);
75 this.socket.onopen = (function(_this) {
76 return function(e) {
77 return _this._onopen(e);
78 };
79 })(this);
80 this.socket.onclose = (function(_this) {
81 return function(e) {
82 return _this._onclose(e);
83 };
84 })(this);
85 this.socket.onmessage = (function(_this) {
86 return function(e) {
87 return _this._onmessage(e);
88 };
89 })(this);
90 return this.socket.onerror = (function(_this) {
91 return function(e) {
92 return _this._onerror(e);
93 };
94 })(this);
95 };
96
97 Connector.prototype.disconnect = function() {
98 this._connectionDesired = false;
99 this._reconnectTimer.stop();
100 if (!this._isSocketConnected()) {
101 return;
102 }
103 this._disconnectionReason = 'manual';
104 return this.socket.close();
105 };
106
107 Connector.prototype._scheduleReconnection = function() {
108 if (!this._connectionDesired) {
109 return;
110 }
111 if (!this._reconnectTimer.running) {
112 this._reconnectTimer.start(this._nextDelay);
113 return this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2);
114 }
115 };
116
117 Connector.prototype.sendCommand = function(command) {
118 if (this.protocol == null) {
119 return;
120 }
121 return this._sendCommand(command);
122 };
123
124 Connector.prototype._sendCommand = function(command) {
125 return this.socket.send(JSON.stringify(command));
126 };
127
128 Connector.prototype._closeOnError = function() {
129 this._handshakeTimeout.stop();
130 this._disconnectionReason = 'error';
131 return this.socket.close();
132 };
133
134 Connector.prototype._onopen = function(e) {
135 var hello;
136 this.handlers.socketConnected();
137 this._disconnectionReason = 'handshake-failed';
138 hello = {
139 command: 'hello',
140 protocols: [PROTOCOL_6, PROTOCOL_7]
141 };
142 hello.ver = Version;
143 if (this.options.ext) {
144 hello.ext = this.options.ext;
145 }
146 if (this.options.extver) {
147 hello.extver = this.options.extver;
148 }
149 if (this.options.snipver) {
150 hello.snipver = this.options.snipver;
151 }
152 this._sendCommand(hello);
153 return this._handshakeTimeout.start(this.options.handshake_timeout);
154 };
155
156 Connector.prototype._onclose = function(e) {
157 this.protocol = 0;
158 this.handlers.disconnected(this._disconnectionReason, this._nextDelay);
159 return this._scheduleReconnection();
160 };
161
162 Connector.prototype._onerror = function(e) {};
163
164 Connector.prototype._onmessage = function(e) {
165 return this.protocolParser.process(e.data);
166 };
167
168 return Connector;
169
170 })();
171
172 }).call(this);
0 (function() {
1 var CustomEvents;
2
3 CustomEvents = {
4 bind: function(element, eventName, handler) {
5 if (element.addEventListener) {
6 return element.addEventListener(eventName, handler, false);
7 } else if (element.attachEvent) {
8 element[eventName] = 1;
9 return element.attachEvent('onpropertychange', function(event) {
10 if (event.propertyName === eventName) {
11 return handler();
12 }
13 });
14 } else {
15 throw new Error("Attempt to attach custom event " + eventName + " to something which isn't a DOMElement");
16 }
17 },
18 fire: function(element, eventName) {
19 var event;
20 if (element.addEventListener) {
21 event = document.createEvent('HTMLEvents');
22 event.initEvent(eventName, true, true);
23 return document.dispatchEvent(event);
24 } else if (element.attachEvent) {
25 if (element[eventName]) {
26 return element[eventName]++;
27 }
28 } else {
29 throw new Error("Attempt to fire custom event " + eventName + " on something which isn't a DOMElement");
30 }
31 }
32 };
33
34 exports.bind = CustomEvents.bind;
35
36 exports.fire = CustomEvents.fire;
37
38 }).call(this);
0 (function() {
1 var LessPlugin;
2
3 module.exports = LessPlugin = (function() {
4 LessPlugin.identifier = 'less';
5
6 LessPlugin.version = '1.0';
7
8 function LessPlugin(window, host) {
9 this.window = window;
10 this.host = host;
11 }
12
13 LessPlugin.prototype.reload = function(path, options) {
14 if (this.window.less && this.window.less.refresh) {
15 if (path.match(/\.less$/i)) {
16 return this.reloadLess(path);
17 }
18 if (options.originalPath.match(/\.less$/i)) {
19 return this.reloadLess(options.originalPath);
20 }
21 }
22 return false;
23 };
24
25 LessPlugin.prototype.reloadLess = function(path) {
26 var link, links, _i, _len;
27 links = (function() {
28 var _i, _len, _ref, _results;
29 _ref = document.getElementsByTagName('link');
30 _results = [];
31 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
32 link = _ref[_i];
33 if (link.href && link.rel.match(/^stylesheet\/less$/i) || (link.rel.match(/stylesheet/i) && link.type.match(/^text\/(x-)?less$/i))) {
34 _results.push(link);
35 }
36 }
37 return _results;
38 })();
39 if (links.length === 0) {
40 return false;
41 }
42 for (_i = 0, _len = links.length; _i < _len; _i++) {
43 link = links[_i];
44 link.href = this.host.generateCacheBustUrl(link.href);
45 }
46 this.host.console.log("LiveReload is asking LESS to recompile all stylesheets");
47 this.window.less.refresh(true);
48 return true;
49 };
50
51 LessPlugin.prototype.analyze = function() {
52 return {
53 disable: !!(this.window.less && this.window.less.refresh)
54 };
55 };
56
57 return LessPlugin;
58
59 })();
60
61 }).call(this);
0 (function() {
1 var Connector, LiveReload, Options, ProtocolError, Reloader, Timer,
2 __hasProp = {}.hasOwnProperty;
3
4 Connector = require('./connector').Connector;
5
6 Timer = require('./timer').Timer;
7
8 Options = require('./options').Options;
9
10 Reloader = require('./reloader').Reloader;
11
12 ProtocolError = require('./protocol').ProtocolError;
13
14 exports.LiveReload = LiveReload = (function() {
15 function LiveReload(window) {
16 var k, v, _ref;
17 this.window = window;
18 this.listeners = {};
19 this.plugins = [];
20 this.pluginIdentifiers = {};
21 this.console = this.window.console && this.window.console.log && this.window.console.error ? this.window.location.href.match(/LR-verbose/) ? this.window.console : {
22 log: function() {},
23 error: this.window.console.error.bind(this.window.console)
24 } : {
25 log: function() {},
26 error: function() {}
27 };
28 if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) {
29 this.console.error("LiveReload disabled because the browser does not seem to support web sockets");
30 return;
31 }
32 if ('LiveReloadOptions' in window) {
33 this.options = new Options();
34 _ref = window['LiveReloadOptions'];
35 for (k in _ref) {
36 if (!__hasProp.call(_ref, k)) continue;
37 v = _ref[k];
38 this.options.set(k, v);
39 }
40 } else {
41 this.options = Options.extract(this.window.document);
42 if (!this.options) {
43 this.console.error("LiveReload disabled because it could not find its own <SCRIPT> tag");
44 return;
45 }
46 }
47 this.reloader = new Reloader(this.window, this.console, Timer);
48 this.connector = new Connector(this.options, this.WebSocket, Timer, {
49 connecting: (function(_this) {
50 return function() {};
51 })(this),
52 socketConnected: (function(_this) {
53 return function() {};
54 })(this),
55 connected: (function(_this) {
56 return function(protocol) {
57 var _base;
58 if (typeof (_base = _this.listeners).connect === "function") {
59 _base.connect();
60 }
61 _this.log("LiveReload is connected to " + _this.options.host + ":" + _this.options.port + " (protocol v" + protocol + ").");
62 return _this.analyze();
63 };
64 })(this),
65 error: (function(_this) {
66 return function(e) {
67 if (e instanceof ProtocolError) {
68 if (typeof console !== "undefined" && console !== null) {
69 return console.log("" + e.message + ".");
70 }
71 } else {
72 if (typeof console !== "undefined" && console !== null) {
73 return console.log("LiveReload internal error: " + e.message);
74 }
75 }
76 };
77 })(this),
78 disconnected: (function(_this) {
79 return function(reason, nextDelay) {
80 var _base;
81 if (typeof (_base = _this.listeners).disconnect === "function") {
82 _base.disconnect();
83 }
84 switch (reason) {
85 case 'cannot-connect':
86 return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + ", will retry in " + nextDelay + " sec.");
87 case 'broken':
88 return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + ", reconnecting in " + nextDelay + " sec.");
89 case 'handshake-timeout':
90 return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake timeout), will retry in " + nextDelay + " sec.");
91 case 'handshake-failed':
92 return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake failed), will retry in " + nextDelay + " sec.");
93 case 'manual':
94 break;
95 case 'error':
96 break;
97 default:
98 return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + " (" + reason + "), reconnecting in " + nextDelay + " sec.");
99 }
100 };
101 })(this),
102 message: (function(_this) {
103 return function(message) {
104 switch (message.command) {
105 case 'reload':
106 return _this.performReload(message);
107 case 'alert':
108 return _this.performAlert(message);
109 }
110 };
111 })(this)
112 });
113 this.initialized = true;
114 }
115
116 LiveReload.prototype.on = function(eventName, handler) {
117 return this.listeners[eventName] = handler;
118 };
119
120 LiveReload.prototype.log = function(message) {
121 return this.console.log("" + message);
122 };
123
124 LiveReload.prototype.performReload = function(message) {
125 var _ref, _ref1, _ref2;
126 this.log("LiveReload received reload request: " + (JSON.stringify(message, null, 2)));
127 return this.reloader.reload(message.path, {
128 liveCSS: (_ref = message.liveCSS) != null ? _ref : true,
129 liveImg: (_ref1 = message.liveImg) != null ? _ref1 : true,
130 reloadMissingCSS: (_ref2 = message.reloadMissingCSS) != null ? _ref2 : true,
131 originalPath: message.originalPath || '',
132 overrideURL: message.overrideURL || '',
133 serverURL: "http://" + this.options.host + ":" + this.options.port
134 });
135 };
136
137 LiveReload.prototype.performAlert = function(message) {
138 return alert(message.message);
139 };
140
141 LiveReload.prototype.shutDown = function() {
142 var _base;
143 if (!this.initialized) {
144 return;
145 }
146 this.connector.disconnect();
147 this.log("LiveReload disconnected.");
148 return typeof (_base = this.listeners).shutdown === "function" ? _base.shutdown() : void 0;
149 };
150
151 LiveReload.prototype.hasPlugin = function(identifier) {
152 return !!this.pluginIdentifiers[identifier];
153 };
154
155 LiveReload.prototype.addPlugin = function(pluginClass) {
156 var plugin;
157 if (!this.initialized) {
158 return;
159 }
160 if (this.hasPlugin(pluginClass.identifier)) {
161 return;
162 }
163 this.pluginIdentifiers[pluginClass.identifier] = true;
164 plugin = new pluginClass(this.window, {
165 _livereload: this,
166 _reloader: this.reloader,
167 _connector: this.connector,
168 console: this.console,
169 Timer: Timer,
170 generateCacheBustUrl: (function(_this) {
171 return function(url) {
172 return _this.reloader.generateCacheBustUrl(url);
173 };
174 })(this)
175 });
176 this.plugins.push(plugin);
177 this.reloader.addPlugin(plugin);
178 };
179
180 LiveReload.prototype.analyze = function() {
181 var plugin, pluginData, pluginsData, _i, _len, _ref;
182 if (!this.initialized) {
183 return;
184 }
185 if (!(this.connector.protocol >= 7)) {
186 return;
187 }
188 pluginsData = {};
189 _ref = this.plugins;
190 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
191 plugin = _ref[_i];
192 pluginsData[plugin.constructor.identifier] = pluginData = (typeof plugin.analyze === "function" ? plugin.analyze() : void 0) || {};
193 pluginData.version = plugin.constructor.version;
194 }
195 this.connector.sendCommand({
196 command: 'info',
197 plugins: pluginsData,
198 url: this.window.location.href
199 });
200 };
201
202 return LiveReload;
203
204 })();
205
206 }).call(this);
0 (function() {
1 var Options;
2
3 exports.Options = Options = (function() {
4 function Options() {
5 this.https = false;
6 this.host = null;
7 this.port = 35729;
8 this.snipver = null;
9 this.ext = null;
10 this.extver = null;
11 this.mindelay = 1000;
12 this.maxdelay = 60000;
13 this.handshake_timeout = 5000;
14 }
15
16 Options.prototype.set = function(name, value) {
17 if (typeof value === 'undefined') {
18 return;
19 }
20 if (!isNaN(+value)) {
21 value = +value;
22 }
23 return this[name] = value;
24 };
25
26 return Options;
27
28 })();
29
30 Options.extract = function(document) {
31 var element, keyAndValue, m, mm, options, pair, src, _i, _j, _len, _len1, _ref, _ref1;
32 _ref = document.getElementsByTagName('script');
33 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
34 element = _ref[_i];
35 if ((src = element.src) && (m = src.match(/^[^:]+:\/\/(.*)\/z?livereload\.js(?:\?(.*))?$/))) {
36 options = new Options();
37 options.https = src.indexOf("https") === 0;
38 if (mm = m[1].match(/^([^\/:]+)(?::(\d+))?$/)) {
39 options.host = mm[1];
40 if (mm[2]) {
41 options.port = parseInt(mm[2], 10);
42 }
43 }
44 if (m[2]) {
45 _ref1 = m[2].split('&');
46 for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
47 pair = _ref1[_j];
48 if ((keyAndValue = pair.split('=')).length > 1) {
49 options.set(keyAndValue[0].replace(/-/g, '_'), keyAndValue.slice(1).join('='));
50 }
51 }
52 }
53 return options;
54 }
55 }
56 return null;
57 };
58
59 }).call(this);
0 (function() {
1 var PROTOCOL_6, PROTOCOL_7, Parser, ProtocolError,
2 __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
3
4 exports.PROTOCOL_6 = PROTOCOL_6 = 'http://livereload.com/protocols/official-6';
5
6 exports.PROTOCOL_7 = PROTOCOL_7 = 'http://livereload.com/protocols/official-7';
7
8 exports.ProtocolError = ProtocolError = (function() {
9 function ProtocolError(reason, data) {
10 this.message = "LiveReload protocol error (" + reason + ") after receiving data: \"" + data + "\".";
11 }
12
13 return ProtocolError;
14
15 })();
16
17 exports.Parser = Parser = (function() {
18 function Parser(handlers) {
19 this.handlers = handlers;
20 this.reset();
21 }
22
23 Parser.prototype.reset = function() {
24 return this.protocol = null;
25 };
26
27 Parser.prototype.process = function(data) {
28 var command, e, message, options, _ref;
29 try {
30 if (this.protocol == null) {
31 if (data.match(/^!!ver:([\d.]+)$/)) {
32 this.protocol = 6;
33 } else if (message = this._parseMessage(data, ['hello'])) {
34 if (!message.protocols.length) {
35 throw new ProtocolError("no protocols specified in handshake message");
36 } else if (__indexOf.call(message.protocols, PROTOCOL_7) >= 0) {
37 this.protocol = 7;
38 } else if (__indexOf.call(message.protocols, PROTOCOL_6) >= 0) {
39 this.protocol = 6;
40 } else {
41 throw new ProtocolError("no supported protocols found");
42 }
43 }
44 return this.handlers.connected(this.protocol);
45 } else if (this.protocol === 6) {
46 message = JSON.parse(data);
47 if (!message.length) {
48 throw new ProtocolError("protocol 6 messages must be arrays");
49 }
50 command = message[0], options = message[1];
51 if (command !== 'refresh') {
52 throw new ProtocolError("unknown protocol 6 command");
53 }
54 return this.handlers.message({
55 command: 'reload',
56 path: options.path,
57 liveCSS: (_ref = options.apply_css_live) != null ? _ref : true
58 });
59 } else {
60 message = this._parseMessage(data, ['reload', 'alert']);
61 return this.handlers.message(message);
62 }
63 } catch (_error) {
64 e = _error;
65 if (e instanceof ProtocolError) {
66 return this.handlers.error(e);
67 } else {
68 throw e;
69 }
70 }
71 };
72
73 Parser.prototype._parseMessage = function(data, validCommands) {
74 var e, message, _ref;
75 try {
76 message = JSON.parse(data);
77 } catch (_error) {
78 e = _error;
79 throw new ProtocolError('unparsable JSON', data);
80 }
81 if (!message.command) {
82 throw new ProtocolError('missing "command" key', data);
83 }
84 if (_ref = message.command, __indexOf.call(validCommands, _ref) < 0) {
85 throw new ProtocolError("invalid command '" + message.command + "', only valid commands are: " + (validCommands.join(', ')) + ")", data);
86 }
87 return message;
88 };
89
90 return Parser;
91
92 })();
93
94 }).call(this);
0 (function() {
1 var IMAGE_STYLES, Reloader, numberOfMatchingSegments, pathFromUrl, pathsMatch, pickBestMatch, splitUrl;
2
3 splitUrl = function(url) {
4 var comboSign, hash, index, params;
5 if ((index = url.indexOf('#')) >= 0) {
6 hash = url.slice(index);
7 url = url.slice(0, index);
8 } else {
9 hash = '';
10 }
11 comboSign = url.indexOf('??');
12 if (comboSign >= 0) {
13 if (comboSign + 1 !== url.lastIndexOf('?')) {
14 index = url.lastIndexOf('?');
15 }
16 } else {
17 index = url.indexOf('?');
18 }
19 if (index >= 0) {
20 params = url.slice(index);
21 url = url.slice(0, index);
22 } else {
23 params = '';
24 }
25 return {
26 url: url,
27 params: params,
28 hash: hash
29 };
30 };
31
32 pathFromUrl = function(url) {
33 var path;
34 url = splitUrl(url).url;
35 if (url.indexOf('file://') === 0) {
36 path = url.replace(/^file:\/\/(localhost)?/, '');
37 } else {
38 path = url.replace(/^([^:]+:)?\/\/([^:\/]+)(:\d*)?\//, '/');
39 }
40 return decodeURIComponent(path);
41 };
42
43 pickBestMatch = function(path, objects, pathFunc) {
44 var bestMatch, object, score, _i, _len;
45 bestMatch = {
46 score: 0
47 };
48 for (_i = 0, _len = objects.length; _i < _len; _i++) {
49 object = objects[_i];
50 score = numberOfMatchingSegments(path, pathFunc(object));
51 if (score > bestMatch.score) {
52 bestMatch = {
53 object: object,
54 score: score
55 };
56 }
57 }
58 if (bestMatch.score > 0) {
59 return bestMatch;
60 } else {
61 return null;
62 }
63 };
64
65 numberOfMatchingSegments = function(path1, path2) {
66 var comps1, comps2, eqCount, len;
67 path1 = path1.replace(/^\/+/, '').toLowerCase();
68 path2 = path2.replace(/^\/+/, '').toLowerCase();
69 if (path1 === path2) {
70 return 10000;
71 }
72 comps1 = path1.split('/').reverse();
73 comps2 = path2.split('/').reverse();
74 len = Math.min(comps1.length, comps2.length);
75 eqCount = 0;
76 while (eqCount < len && comps1[eqCount] === comps2[eqCount]) {
77 ++eqCount;
78 }
79 return eqCount;
80 };
81
82 pathsMatch = function(path1, path2) {
83 return numberOfMatchingSegments(path1, path2) > 0;
84 };
85
86 IMAGE_STYLES = [
87 {
88 selector: 'background',
89 styleNames: ['backgroundImage']
90 }, {
91 selector: 'border',
92 styleNames: ['borderImage', 'webkitBorderImage', 'MozBorderImage']
93 }
94 ];
95
96 exports.Reloader = Reloader = (function() {
97 function Reloader(window, console, Timer) {
98 this.window = window;
99 this.console = console;
100 this.Timer = Timer;
101 this.document = this.window.document;
102 this.importCacheWaitPeriod = 200;
103 this.plugins = [];
104 }
105
106 Reloader.prototype.addPlugin = function(plugin) {
107 return this.plugins.push(plugin);
108 };
109
110 Reloader.prototype.analyze = function(callback) {
111 return results;
112 };
113
114 Reloader.prototype.reload = function(path, options) {
115 var plugin, _base, _i, _len, _ref;
116 this.options = options;
117 if ((_base = this.options).stylesheetReloadTimeout == null) {
118 _base.stylesheetReloadTimeout = 15000;
119 }
120 _ref = this.plugins;
121 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
122 plugin = _ref[_i];
123 if (plugin.reload && plugin.reload(path, options)) {
124 return;
125 }
126 }
127 if (options.liveCSS && path.match(/\.css(?:\.map)?$/i)) {
128 if (this.reloadStylesheet(path)) {
129 return;
130 }
131 }
132 if (options.liveImg && path.match(/\.(jpe?g|png|gif)$/i)) {
133 this.reloadImages(path);
134 return;
135 }
136 if (options.isChromeExtension) {
137 this.reloadChromeExtension();
138 return;
139 }
140 return this.reloadPage();
141 };
142
143 Reloader.prototype.reloadPage = function() {
144 return this.window.document.location.reload();
145 };
146
147 Reloader.prototype.reloadChromeExtension = function() {
148 return this.window.chrome.runtime.reload();
149 };
150
151 Reloader.prototype.reloadImages = function(path) {
152 var expando, img, selector, styleNames, styleSheet, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _results;
153 expando = this.generateUniqueString();
154 _ref = this.document.images;
155 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
156 img = _ref[_i];
157 if (pathsMatch(path, pathFromUrl(img.src))) {
158 img.src = this.generateCacheBustUrl(img.src, expando);
159 }
160 }
161 if (this.document.querySelectorAll) {
162 for (_j = 0, _len1 = IMAGE_STYLES.length; _j < _len1; _j++) {
163 _ref1 = IMAGE_STYLES[_j], selector = _ref1.selector, styleNames = _ref1.styleNames;
164 _ref2 = this.document.querySelectorAll("[style*=" + selector + "]");
165 for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
166 img = _ref2[_k];
167 this.reloadStyleImages(img.style, styleNames, path, expando);
168 }
169 }
170 }
171 if (this.document.styleSheets) {
172 _ref3 = this.document.styleSheets;
173 _results = [];
174 for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
175 styleSheet = _ref3[_l];
176 _results.push(this.reloadStylesheetImages(styleSheet, path, expando));
177 }
178 return _results;
179 }
180 };
181
182 Reloader.prototype.reloadStylesheetImages = function(styleSheet, path, expando) {
183 var e, rule, rules, styleNames, _i, _j, _len, _len1;
184 try {
185 rules = styleSheet != null ? styleSheet.cssRules : void 0;
186 } catch (_error) {
187 e = _error;
188 }
189 if (!rules) {
190 return;
191 }
192 for (_i = 0, _len = rules.length; _i < _len; _i++) {
193 rule = rules[_i];
194 switch (rule.type) {
195 case CSSRule.IMPORT_RULE:
196 this.reloadStylesheetImages(rule.styleSheet, path, expando);
197 break;
198 case CSSRule.STYLE_RULE:
199 for (_j = 0, _len1 = IMAGE_STYLES.length; _j < _len1; _j++) {
200 styleNames = IMAGE_STYLES[_j].styleNames;
201 this.reloadStyleImages(rule.style, styleNames, path, expando);
202 }
203 break;
204 case CSSRule.MEDIA_RULE:
205 this.reloadStylesheetImages(rule, path, expando);
206 }
207 }
208 };
209
210 Reloader.prototype.reloadStyleImages = function(style, styleNames, path, expando) {
211 var newValue, styleName, value, _i, _len;
212 for (_i = 0, _len = styleNames.length; _i < _len; _i++) {
213 styleName = styleNames[_i];
214 value = style[styleName];
215 if (typeof value === 'string') {
216 newValue = value.replace(/\burl\s*\(([^)]*)\)/, (function(_this) {
217 return function(match, src) {
218 if (pathsMatch(path, pathFromUrl(src))) {
219 return "url(" + (_this.generateCacheBustUrl(src, expando)) + ")";
220 } else {
221 return match;
222 }
223 };
224 })(this));
225 if (newValue !== value) {
226 style[styleName] = newValue;
227 }
228 }
229 }
230 };
231
232 Reloader.prototype.reloadStylesheet = function(path) {
233 var imported, link, links, match, style, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1;
234 links = (function() {
235 var _i, _len, _ref, _results;
236 _ref = this.document.getElementsByTagName('link');
237 _results = [];
238 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
239 link = _ref[_i];
240 if (link.rel.match(/^stylesheet$/i) && !link.__LiveReload_pendingRemoval) {
241 _results.push(link);
242 }
243 }
244 return _results;
245 }).call(this);
246 imported = [];
247 _ref = this.document.getElementsByTagName('style');
248 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
249 style = _ref[_i];
250 if (style.sheet) {
251 this.collectImportedStylesheets(style, style.sheet, imported);
252 }
253 }
254 for (_j = 0, _len1 = links.length; _j < _len1; _j++) {
255 link = links[_j];
256 this.collectImportedStylesheets(link, link.sheet, imported);
257 }
258 if (this.window.StyleFix && this.document.querySelectorAll) {
259 _ref1 = this.document.querySelectorAll('style[data-href]');
260 for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
261 style = _ref1[_k];
262 links.push(style);
263 }
264 }
265 this.console.log("LiveReload found " + links.length + " LINKed stylesheets, " + imported.length + " @imported stylesheets");
266 match = pickBestMatch(path, links.concat(imported), (function(_this) {
267 return function(l) {
268 return pathFromUrl(_this.linkHref(l));
269 };
270 })(this));
271 if (match) {
272 if (match.object.rule) {
273 this.console.log("LiveReload is reloading imported stylesheet: " + match.object.href);
274 this.reattachImportedRule(match.object);
275 } else {
276 this.console.log("LiveReload is reloading stylesheet: " + (this.linkHref(match.object)));
277 this.reattachStylesheetLink(match.object);
278 }
279 } else {
280 if (this.options.reloadMissingCSS) {
281 this.console.log("LiveReload will reload all stylesheets because path '" + path + "' did not match any specific one. To disable this behavior, set 'options.reloadMissingCSS' to 'false'.");
282 for (_l = 0, _len3 = links.length; _l < _len3; _l++) {
283 link = links[_l];
284 this.reattachStylesheetLink(link);
285 }
286 } else {
287 this.console.log("LiveReload will not reload path '" + path + "' because the stylesheet was not found on the page and 'options.reloadMissingCSS' was set to 'false'.");
288 }
289 }
290 return true;
291 };
292
293 Reloader.prototype.collectImportedStylesheets = function(link, styleSheet, result) {
294 var e, index, rule, rules, _i, _len;
295 try {
296 rules = styleSheet != null ? styleSheet.cssRules : void 0;
297 } catch (_error) {
298 e = _error;
299 }
300 if (rules && rules.length) {
301 for (index = _i = 0, _len = rules.length; _i < _len; index = ++_i) {
302 rule = rules[index];
303 switch (rule.type) {
304 case CSSRule.CHARSET_RULE:
305 continue;
306 case CSSRule.IMPORT_RULE:
307 result.push({
308 link: link,
309 rule: rule,
310 index: index,
311 href: rule.href
312 });
313 this.collectImportedStylesheets(link, rule.styleSheet, result);
314 break;
315 default:
316 break;
317 }
318 }
319 }
320 };
321
322 Reloader.prototype.waitUntilCssLoads = function(clone, func) {
323 var callbackExecuted, executeCallback, poll;
324 callbackExecuted = false;
325 executeCallback = (function(_this) {
326 return function() {
327 if (callbackExecuted) {
328 return;
329 }
330 callbackExecuted = true;
331 return func();
332 };
333 })(this);
334 clone.onload = (function(_this) {
335 return function() {
336 _this.console.log("LiveReload: the new stylesheet has finished loading");
337 _this.knownToSupportCssOnLoad = true;
338 return executeCallback();
339 };
340 })(this);
341 if (!this.knownToSupportCssOnLoad) {
342 (poll = (function(_this) {
343 return function() {
344 if (clone.sheet) {
345 _this.console.log("LiveReload is polling until the new CSS finishes loading...");
346 return executeCallback();
347 } else {
348 return _this.Timer.start(50, poll);
349 }
350 };
351 })(this))();
352 }
353 return this.Timer.start(this.options.stylesheetReloadTimeout, executeCallback);
354 };
355
356 Reloader.prototype.linkHref = function(link) {
357 return link.href || link.getAttribute('data-href');
358 };
359
360 Reloader.prototype.reattachStylesheetLink = function(link) {
361 var clone, parent;
362 if (link.__LiveReload_pendingRemoval) {
363 return;
364 }
365 link.__LiveReload_pendingRemoval = true;
366 if (link.tagName === 'STYLE') {
367 clone = this.document.createElement('link');
368 clone.rel = 'stylesheet';
369 clone.media = link.media;
370 clone.disabled = link.disabled;
371 } else {
372 clone = link.cloneNode(false);
373 }
374 clone.href = this.generateCacheBustUrl(this.linkHref(link));
375 parent = link.parentNode;
376 if (parent.lastChild === link) {
377 parent.appendChild(clone);
378 } else {
379 parent.insertBefore(clone, link.nextSibling);
380 }
381 return this.waitUntilCssLoads(clone, (function(_this) {
382 return function() {
383 var additionalWaitingTime;
384 if (/AppleWebKit/.test(navigator.userAgent)) {
385 additionalWaitingTime = 5;
386 } else {
387 additionalWaitingTime = 200;
388 }
389 return _this.Timer.start(additionalWaitingTime, function() {
390 var _ref;
391 if (!link.parentNode) {
392 return;
393 }
394 link.parentNode.removeChild(link);
395 clone.onreadystatechange = null;
396 return (_ref = _this.window.StyleFix) != null ? _ref.link(clone) : void 0;
397 });
398 };
399 })(this));
400 };
401
402 Reloader.prototype.reattachImportedRule = function(_arg) {
403 var href, index, link, media, newRule, parent, rule, tempLink;
404 rule = _arg.rule, index = _arg.index, link = _arg.link;
405 parent = rule.parentStyleSheet;
406 href = this.generateCacheBustUrl(rule.href);
407 media = rule.media.length ? [].join.call(rule.media, ', ') : '';
408 newRule = "@import url(\"" + href + "\") " + media + ";";
409 rule.__LiveReload_newHref = href;
410 tempLink = this.document.createElement("link");
411 tempLink.rel = 'stylesheet';
412 tempLink.href = href;
413 tempLink.__LiveReload_pendingRemoval = true;
414 if (link.parentNode) {
415 link.parentNode.insertBefore(tempLink, link);
416 }
417 return this.Timer.start(this.importCacheWaitPeriod, (function(_this) {
418 return function() {
419 if (tempLink.parentNode) {
420 tempLink.parentNode.removeChild(tempLink);
421 }
422 if (rule.__LiveReload_newHref !== href) {
423 return;
424 }
425 parent.insertRule(newRule, index);
426 parent.deleteRule(index + 1);
427 rule = parent.cssRules[index];
428 rule.__LiveReload_newHref = href;
429 return _this.Timer.start(_this.importCacheWaitPeriod, function() {
430 if (rule.__LiveReload_newHref !== href) {
431 return;
432 }
433 parent.insertRule(newRule, index);
434 return parent.deleteRule(index + 1);
435 });
436 };
437 })(this));
438 };
439
440 Reloader.prototype.generateUniqueString = function() {
441 return 'livereload=' + Date.now();
442 };
443
444 Reloader.prototype.generateCacheBustUrl = function(url, expando) {
445 var hash, oldParams, originalUrl, params, _ref;
446 if (expando == null) {
447 expando = this.generateUniqueString();
448 }
449 _ref = splitUrl(url), url = _ref.url, hash = _ref.hash, oldParams = _ref.params;
450 if (this.options.overrideURL) {
451 if (url.indexOf(this.options.serverURL) < 0) {
452 originalUrl = url;
453 url = this.options.serverURL + this.options.overrideURL + "?url=" + encodeURIComponent(url);
454 this.console.log("LiveReload is overriding source URL " + originalUrl + " with " + url);
455 }
456 }
457 params = oldParams.replace(/(\?|&)livereload=(\d+)/, function(match, sep) {
458 return "" + sep + expando;
459 });
460 if (params === oldParams) {
461 if (oldParams.length === 0) {
462 params = "?" + expando;
463 } else {
464 params = "" + oldParams + "&" + expando;
465 }
466 }
467 return url + params + hash;
468 };
469
470 return Reloader;
471
472 })();
473
474 }).call(this);
0 (function() {
1 var CustomEvents, LiveReload, k;
2
3 CustomEvents = require('./customevents');
4
5 LiveReload = window.LiveReload = new (require('./livereload').LiveReload)(window);
6
7 for (k in window) {
8 if (k.match(/^LiveReloadPlugin/)) {
9 LiveReload.addPlugin(window[k]);
10 }
11 }
12
13 LiveReload.addPlugin(require('./less'));
14
15 LiveReload.on('shutdown', function() {
16 return delete window.LiveReload;
17 });
18
19 LiveReload.on('connect', function() {
20 return CustomEvents.fire(document, 'LiveReloadConnect');
21 });
22
23 LiveReload.on('disconnect', function() {
24 return CustomEvents.fire(document, 'LiveReloadDisconnect');
25 });
26
27 CustomEvents.bind(document, 'LiveReloadShutDown', function() {
28 return LiveReload.shutDown();
29 });
30
31 }).call(this);
0 (function() {
1 var Timer;
2
3 exports.Timer = Timer = (function() {
4 function Timer(func) {
5 this.func = func;
6 this.running = false;
7 this.id = null;
8 this._handler = (function(_this) {
9 return function() {
10 _this.running = false;
11 _this.id = null;
12 return _this.func();
13 };
14 })(this);
15 }
16
17 Timer.prototype.start = function(timeout) {
18 if (this.running) {
19 clearTimeout(this.id);
20 }
21 this.id = setTimeout(this._handler, timeout);
22 return this.running = true;
23 };
24
25 Timer.prototype.stop = function() {
26 if (this.running) {
27 clearTimeout(this.id);
28 this.running = false;
29 return this.id = null;
30 }
31 };
32
33 return Timer;
34
35 })();
36
37 Timer.start = function(timeout, func) {
38 return setTimeout(func, timeout);
39 };
40
41 }).call(this);
0 {
1 "name": "livereload-js",
2 "version": "2.3.0",
3 "description": "LiveReload JS client - auto reload browser on changes",
4 "main": "lib/startup.js",
5 "directories": {
6 "test": "test"
7 },
8 "devDependencies": {
9 "coffee-script": "~1.7.1",
10 "grunt": "^0.4.5",
11 "grunt-browserify": "^3.3.0",
12 "grunt-cli": "^1.2.0",
13 "grunt-contrib-coffee": "^0.12.0",
14 "grunt-mocha-test": "^0.12.6",
15 "jsdom": "~11.5.1",
16 "mocha": "^4.1.0"
17 },
18 "scripts": {
19 "prepare": "grunt build",
20 "test": "grunt test"
21 },
22 "repository": {
23 "type": "git",
24 "url": "git://github.com/livereload/livereload-js.git"
25 },
26 "license": "MIT",
27 "bugs": {
28 "url": "https://github.com/livereload/livereload-js/issues"
29 },
30 "homepage": "https://github.com/livereload/livereload-js"
31 }