embed source for livereload.js
Pirate Praveen
5 years ago
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 | } |