feat(SublimeText2.EditorPackages): cache packages

This commit is contained in:
Iristyle
2013-04-04 08:55:15 -04:00
parent d65666cdfc
commit c3efdad2c2
274 changed files with 26863 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
{
"version" : "1.6"
, "port" : 35729
, "delay_ms" : 100
, "apply_js_live" : false
, "apply_css_live" : true
, "apply_images_live" : true
}

View File

@@ -0,0 +1,39 @@
[
{
"caption": "Preferences",
"mnemonic": "n",
"id": "preferences",
"children":
[
{
"caption": "Package Settings",
"mnemonic": "P",
"id": "package-settings",
"children":
[
{
"caption": "LiveReload",
"children":
[
{
"command": "open_file", "args":
{
"file": "${packages}/LiveReload/LiveReload.sublime-settings"
},
"caption": "Settings Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/LiveReload.sublime-settings"
},
"caption": "Settings User"
},
{ "caption": "-" }
]
}
]
}
]
}
]

View File

@@ -0,0 +1,62 @@
LiveReload for Sublime Text 2
=========
A web browser page reloading plugin for the [Sublime Text 2](http://sublimetext.com "Sublime Text 2") editor.
Installing
-----
Install with [Sublime Package Control](http://wbond.net/sublime_packages/package_control "Sublime Package Control"), search for LiveReload and install.
Devel branch
-----
Have a look at [devel version](https://github.com/dz0ny/LiveReload-sublimetext2/tree/devel). Which is total rewrite of plugin, supporting SublimeText 3, plugins and much more.
Browser extensions
-----
You can use both major LiveReload versions. For old one you can find instructions bellow, for new ones please visit [New browser extensions](http://help.livereload.com/kb/general-use/browser-extensions "New browser extensions") or try [self loading version](http://help.livereload.com/kb/general-use/using-livereload-without-browser-extensions "self loading version").
### [Google Chrome extension](https://chrome.google.com/extensions/detail/jnihajbhpnppcggbcgedagnkighmdlei)
![](https://github.com/mockko/livereload/raw/master/docs/images/chrome-install-prompt.png)
Click “Install”. Actually, LiveReload does not access your browser history. The warning is misleading.
![](https://github.com/mockko/livereload/raw/master/docs/images/chrome-button.png)
If you want to use it with local files, be sure to enable “Allow access to file URLs” checkbox in Tools > Extensions > LiveReload after installation.
### Safari extension
For now it only works with self loading version:
<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script>
### [Firefox 4 extension](http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions-)
![](http://static-cdn.addons.mozilla.net/img/uploads/previews/full/63/63478.png?modified=1317506904)
## Usage
Now, if you are using Safari, right-click the page you want to be livereload'ed and choose “Enable LiveReload”:
![](https://github.com/mockko/livereload/raw/master/docs/images/safari-context-menu.png)
If you are using Chrome, just click the toolbar button (it will turn green to indicate that LiveReload is active).
----
You can also use the Preferences menu to change port, version and type of reloading(full, js,css).
## Compass
![](http://cdn.nmecdesign.com/wp/wp-content/uploads/2011/12/Compass-Logo.png)
Want to use Livereload with compass ? Now you can !
.scss and .css have to be in the same directory , if no config.rb file is found one will automatically generated !
So if you want to start using compass, install compass gem, edit a xxx.scss file and voila ! A .css file would be automatically generated.

View File

@@ -0,0 +1,8 @@
http_path = "/"
css_dir = "."
sass_dir = "."
images_dir = "img"
javascripts_dir = "js"
output_style = :compressed
relative_assets=true
line_comments = false

View File

@@ -0,0 +1,942 @@
(function() {
var __customevents = {}, __protocol = {}, __connector = {}, __timer = {}, __options = {}, __reloader = {}, __livereload = {}, __less = {}, __startup = {};
// customevents
var CustomEvents;
CustomEvents = {
bind: function(element, eventName, handler) {
if (element.addEventListener) {
return element.addEventListener(eventName, handler, false);
} else if (element.attachEvent) {
element[eventName] = 1;
return element.attachEvent('onpropertychange', function(event) {
if (event.propertyName === eventName) {
return handler();
}
});
} else {
throw new Error("Attempt to attach custom event " + eventName + " to something which isn't a DOMElement");
}
},
fire: function(element, eventName) {
var event;
if (element.addEventListener) {
event = document.createEvent('HTMLEvents');
event.initEvent(eventName, true, true);
return document.dispatchEvent(event);
} else if (element.attachEvent) {
if (element[eventName]) {
return element[eventName]++;
}
} else {
throw new Error("Attempt to fire custom event " + eventName + " on something which isn't a DOMElement");
}
}
};
__customevents.bind = CustomEvents.bind;
__customevents.fire = CustomEvents.fire;
// protocol
var PROTOCOL_6, PROTOCOL_7, Parser, ProtocolError;
var __indexOf = Array.prototype.indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (this[i] === item) return i;
}
return -1;
};
__protocol.PROTOCOL_6 = PROTOCOL_6 = 'http://livereload.com/protocols/official-6';
__protocol.PROTOCOL_7 = PROTOCOL_7 = 'http://livereload.com/protocols/official-7';
__protocol.ProtocolError = ProtocolError = (function() {
function ProtocolError(reason, data) {
this.message = "LiveReload protocol error (" + reason + ") after receiving data: \"" + data + "\".";
}
return ProtocolError;
})();
__protocol.Parser = Parser = (function() {
function Parser(handlers) {
this.handlers = handlers;
this.reset();
}
Parser.prototype.reset = function() {
return this.protocol = null;
};
Parser.prototype.process = function(data) {
var command, message, options, _ref;
try {
if (!(this.protocol != null)) {
if (data.match(/^!!ver:([\d.]+)$/)) {
this.protocol = 6;
} else if (message = this._parseMessage(data, ['hello'])) {
if (!message.protocols.length) {
throw new ProtocolError("no protocols specified in handshake message");
} else if (__indexOf.call(message.protocols, PROTOCOL_7) >= 0) {
this.protocol = 7;
} else if (__indexOf.call(message.protocols, PROTOCOL_6) >= 0) {
this.protocol = 6;
} else {
throw new ProtocolError("no supported protocols found");
}
}
return this.handlers.connected(this.protocol);
} else if (this.protocol === 6) {
message = JSON.parse(data);
if (!message.length) {
throw new ProtocolError("protocol 6 messages must be arrays");
}
command = message[0], options = message[1];
if (command !== 'refresh') {
throw new ProtocolError("unknown protocol 6 command");
}
return this.handlers.message({
command: 'reload',
path: options.path,
liveCSS: (_ref = options.apply_css_live) != null ? _ref : true
});
} else {
message = this._parseMessage(data, ['reload', 'alert']);
return this.handlers.message(message);
}
} catch (e) {
if (e instanceof ProtocolError) {
return this.handlers.error(e);
} else {
throw e;
}
}
};
Parser.prototype._parseMessage = function(data, validCommands) {
var message, _ref;
try {
message = JSON.parse(data);
} catch (e) {
throw new ProtocolError('unparsable JSON', data);
}
if (!message.command) {
throw new ProtocolError('missing "command" key', data);
}
if (_ref = message.command, __indexOf.call(validCommands, _ref) < 0) {
throw new ProtocolError("invalid command '" + message.command + "', only valid commands are: " + (validCommands.join(', ')) + ")", data);
}
return message;
};
return Parser;
})();
// connector
var Connector, PROTOCOL_6, PROTOCOL_7, Parser, Version, _ref;
_ref = __protocol, Parser = _ref.Parser, PROTOCOL_6 = _ref.PROTOCOL_6, PROTOCOL_7 = _ref.PROTOCOL_7;
Version = '2.0.7';
__connector.Connector = Connector = (function() {
function Connector(options, WebSocket, Timer, handlers) {
var _this = this;
this.options = options;
this.WebSocket = WebSocket;
this.Timer = Timer;
this.handlers = handlers;
this._uri = "ws://" + this.options.host + ":" + this.options.port + "/livereload";
this._nextDelay = this.options.mindelay;
this._connectionDesired = false;
this.protocol = 0;
this.protocolParser = new Parser({
connected: function(protocol) {
_this.protocol = protocol;
_this._handshakeTimeout.stop();
_this._nextDelay = _this.options.mindelay;
_this._disconnectionReason = 'broken';
return _this.handlers.connected(protocol);
},
error: function(e) {
_this.handlers.error(e);
return _this._closeOnError();
},
message: function(message) {
return _this.handlers.message(message);
}
});
this._handshakeTimeout = new Timer(function() {
if (!_this._isSocketConnected()) return;
_this._disconnectionReason = 'handshake-timeout';
return _this.socket.close();
});
this._reconnectTimer = new Timer(function() {
if (!_this._connectionDesired) return;
return _this.connect();
});
this.connect();
}
Connector.prototype._isSocketConnected = function() {
return this.socket && this.socket.readyState === this.WebSocket.OPEN;
};
Connector.prototype.connect = function() {
var _this = this;
this._connectionDesired = true;
if (this._isSocketConnected()) return;
this._reconnectTimer.stop();
this._disconnectionReason = 'cannot-connect';
this.protocolParser.reset();
this.handlers.connecting();
this.socket = new this.WebSocket(this._uri);
this.socket.onopen = function(e) {
return _this._onopen(e);
};
this.socket.onclose = function(e) {
return _this._onclose(e);
};
this.socket.onmessage = function(e) {
return _this._onmessage(e);
};
return this.socket.onerror = function(e) {
return _this._onerror(e);
};
};
Connector.prototype.disconnect = function() {
this._connectionDesired = false;
this._reconnectTimer.stop();
if (!this._isSocketConnected()) return;
this._disconnectionReason = 'manual';
return this.socket.close();
};
Connector.prototype._scheduleReconnection = function() {
if (!this._connectionDesired) return;
if (!this._reconnectTimer.running) {
this._reconnectTimer.start(this._nextDelay);
return this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2);
}
};
Connector.prototype.sendCommand = function(command) {
if (this.protocol == null) return;
return this._sendCommand(command);
};
Connector.prototype._sendCommand = function(command) {
return this.socket.send(JSON.stringify(command));
};
Connector.prototype._closeOnError = function() {
this._handshakeTimeout.stop();
this._disconnectionReason = 'error';
return this.socket.close();
};
Connector.prototype._onopen = function(e) {
var hello;
this.handlers.socketConnected();
this._disconnectionReason = 'handshake-failed';
hello = {
command: 'hello',
protocols: [PROTOCOL_6, PROTOCOL_7]
};
hello.ver = Version;
if (this.options.ext) hello.ext = this.options.ext;
if (this.options.extver) hello.extver = this.options.extver;
if (this.options.snipver) hello.snipver = this.options.snipver;
this._sendCommand(hello);
return this._handshakeTimeout.start(this.options.handshake_timeout);
};
Connector.prototype._onclose = function(e) {
this.protocol = 0;
this.handlers.disconnected(this._disconnectionReason, this._nextDelay);
return this._scheduleReconnection();
};
Connector.prototype._onerror = function(e) {};
Connector.prototype._onmessage = function(e) {
return this.protocolParser.process(e.data);
};
return Connector;
})();
// timer
var Timer;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
__timer.Timer = Timer = (function() {
function Timer(func) {
this.func = func;
this.running = false;
this.id = null;
this._handler = __bind(function() {
this.running = false;
this.id = null;
return this.func();
}, this);
}
Timer.prototype.start = function(timeout) {
if (this.running) {
clearTimeout(this.id);
}
this.id = setTimeout(this._handler, timeout);
return this.running = true;
};
Timer.prototype.stop = function() {
if (this.running) {
clearTimeout(this.id);
this.running = false;
return this.id = null;
}
};
return Timer;
})();
Timer.start = function(timeout, func) {
return setTimeout(func, timeout);
};
// options
var Options;
__options.Options = Options = (function() {
function Options() {
this.host = null;
this.port = 35729;
this.snipver = null;
this.ext = null;
this.extver = null;
this.mindelay = 1000;
this.maxdelay = 60000;
this.handshake_timeout = 5000;
}
Options.prototype.set = function(name, value) {
switch (typeof this[name]) {
case 'undefined':
break;
case 'number':
return this[name] = +value;
default:
return this[name] = value;
}
};
return Options;
})();
Options.extract = function(document) {
var element, keyAndValue, m, mm, options, pair, src, _i, _j, _len, _len2, _ref, _ref2;
_ref = document.getElementsByTagName('script');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
element = _ref[_i];
if ((src = element.src) && (m = src.match(/^[^:]+:\/\/(.*)\/z?livereload\.js(?:\?(.*))?$/))) {
options = new Options();
if (mm = m[1].match(/^([^\/:]+)(?::(\d+))?$/)) {
options.host = mm[1];
if (mm[2]) {
options.port = parseInt(mm[2], 10);
}
}
if (m[2]) {
_ref2 = m[2].split('&');
for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
pair = _ref2[_j];
if ((keyAndValue = pair.split('=')).length > 1) {
options.set(keyAndValue[0].replace(/-/g, '_'), keyAndValue.slice(1).join('='));
}
}
}
return options;
}
}
return null;
};
// reloader
var IMAGE_STYLES, Reloader, numberOfMatchingSegments, pathFromUrl, pathsMatch, pickBestMatch, splitUrl;
splitUrl = function(url) {
var hash, index, params;
if ((index = url.indexOf('#')) >= 0) {
hash = url.slice(index);
url = url.slice(0, index);
} else {
hash = '';
}
if ((index = url.indexOf('?')) >= 0) {
params = url.slice(index);
url = url.slice(0, index);
} else {
params = '';
}
return {
url: url,
params: params,
hash: hash
};
};
pathFromUrl = function(url) {
var path;
url = splitUrl(url).url;
if (url.indexOf('file://') === 0) {
path = url.replace(/^file:\/\/(localhost)?/, '');
} else {
path = url.replace(/^([^:]+:)?\/\/([^:\/]+)(:\d*)?\//, '/');
}
return decodeURIComponent(path);
};
pickBestMatch = function(path, objects, pathFunc) {
var bestMatch, object, score, _i, _len;
bestMatch = {
score: 0
};
for (_i = 0, _len = objects.length; _i < _len; _i++) {
object = objects[_i];
score = numberOfMatchingSegments(path, pathFunc(object));
if (score > bestMatch.score) {
bestMatch = {
object: object,
score: score
};
}
}
if (bestMatch.score > 0) {
return bestMatch;
} else {
return null;
}
};
numberOfMatchingSegments = function(path1, path2) {
var comps1, comps2, eqCount, len;
path1 = path1.replace(/^\/+/, '').toLowerCase();
path2 = path2.replace(/^\/+/, '').toLowerCase();
if (path1 === path2) return 10000;
comps1 = path1.split('/').reverse();
comps2 = path2.split('/').reverse();
len = Math.min(comps1.length, comps2.length);
eqCount = 0;
while (eqCount < len && comps1[eqCount] === comps2[eqCount]) {
++eqCount;
}
return eqCount;
};
pathsMatch = function(path1, path2) {
return numberOfMatchingSegments(path1, path2) > 0;
};
IMAGE_STYLES = [
{
selector: 'background',
styleNames: ['backgroundImage']
}, {
selector: 'border',
styleNames: ['borderImage', 'webkitBorderImage', 'MozBorderImage']
}
];
__reloader.Reloader = Reloader = (function() {
function Reloader(window, console, Timer) {
this.window = window;
this.console = console;
this.Timer = Timer;
this.document = this.window.document;
this.stylesheetGracePeriod = 200;
this.importCacheWaitPeriod = 200;
this.plugins = [];
}
Reloader.prototype.addPlugin = function(plugin) {
return this.plugins.push(plugin);
};
Reloader.prototype.analyze = function(callback) {
return results;
};
Reloader.prototype.reload = function(path, options) {
var plugin, _i, _len, _ref;
this.options = options;
_ref = this.plugins;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
plugin = _ref[_i];
if (plugin.reload && plugin.reload(path, options)) return;
}
if (options.liveCSS) {
if (path.match(/\.css$/i)) if (this.reloadStylesheet(path)) return;
}
if (options.liveImg) {
if (path.match(/\.(jpe?g|png|gif)$/i)) {
this.reloadImages(path);
return;
}
}
return this.reloadPage();
};
Reloader.prototype.reloadPage = function() {
return this.window.document.location.reload();
};
Reloader.prototype.reloadImages = function(path) {
var expando, img, selector, styleNames, styleSheet, _i, _j, _k, _l, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results;
expando = this.generateUniqueString();
_ref = this.document.images;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
img = _ref[_i];
if (pathsMatch(path, pathFromUrl(img.src))) {
img.src = this.generateCacheBustUrl(img.src, expando);
}
}
if (this.document.querySelectorAll) {
for (_j = 0, _len2 = IMAGE_STYLES.length; _j < _len2; _j++) {
_ref2 = IMAGE_STYLES[_j], selector = _ref2.selector, styleNames = _ref2.styleNames;
_ref3 = this.document.querySelectorAll("[style*=" + selector + "]");
for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) {
img = _ref3[_k];
this.reloadStyleImages(img.style, styleNames, path, expando);
}
}
}
if (this.document.styleSheets) {
_ref4 = this.document.styleSheets;
_results = [];
for (_l = 0, _len4 = _ref4.length; _l < _len4; _l++) {
styleSheet = _ref4[_l];
_results.push(this.reloadStylesheetImages(styleSheet, path, expando));
}
return _results;
}
};
Reloader.prototype.reloadStylesheetImages = function(styleSheet, path, expando) {
var rule, rules, styleNames, _i, _j, _len, _len2;
try {
rules = styleSheet != null ? styleSheet.cssRules : void 0;
} catch (e) {
}
if (!rules) return;
for (_i = 0, _len = rules.length; _i < _len; _i++) {
rule = rules[_i];
switch (rule.type) {
case CSSRule.IMPORT_RULE:
this.reloadStylesheetImages(rule.styleSheet, path, expando);
break;
case CSSRule.STYLE_RULE:
for (_j = 0, _len2 = IMAGE_STYLES.length; _j < _len2; _j++) {
styleNames = IMAGE_STYLES[_j].styleNames;
this.reloadStyleImages(rule.style, styleNames, path, expando);
}
break;
case CSSRule.MEDIA_RULE:
this.reloadStylesheetImages(rule, path, expando);
}
}
};
Reloader.prototype.reloadStyleImages = function(style, styleNames, path, expando) {
var newValue, styleName, value, _i, _len;
var _this = this;
for (_i = 0, _len = styleNames.length; _i < _len; _i++) {
styleName = styleNames[_i];
value = style[styleName];
if (typeof value === 'string') {
newValue = value.replace(/\burl\s*\(([^)]*)\)/, function(match, src) {
if (pathsMatch(path, pathFromUrl(src))) {
return "url(" + (_this.generateCacheBustUrl(src, expando)) + ")";
} else {
return match;
}
});
if (newValue !== value) style[styleName] = newValue;
}
}
};
Reloader.prototype.reloadStylesheet = function(path) {
var imported, link, links, match, style, _i, _j, _k, _len, _len2, _len3, _ref;
links = (function() {
var _i, _len, _ref, _results;
_ref = this.document.getElementsByTagName('link');
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
if (link.rel === 'stylesheet' && !link.__LiveReload_pendingRemoval) {
_results.push(link);
}
}
return _results;
}).call(this);
imported = [];
_ref = this.document.getElementsByTagName('style');
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
style = _ref[_i];
if (style.sheet) {
this.collectImportedStylesheets(style, style.sheet, imported);
}
}
for (_j = 0, _len2 = links.length; _j < _len2; _j++) {
link = links[_j];
this.collectImportedStylesheets(link, link.sheet, imported);
}
this.console.log("LiveReload found " + links.length + " LINKed stylesheets, " + imported.length + " @imported stylesheets");
match = pickBestMatch(path, links.concat(imported), function(l) {
return pathFromUrl(l.href);
});
if (match) {
if (match.object.rule) {
this.console.log("LiveReload is reloading imported stylesheet: " + match.object.href);
this.reattachImportedRule(match.object);
} else {
this.console.log("LiveReload is reloading stylesheet: " + match.object.href);
this.reattachStylesheetLink(match.object);
}
} else {
this.console.log("LiveReload will reload all stylesheets because path '" + path + "' did not match any specific one");
for (_k = 0, _len3 = links.length; _k < _len3; _k++) {
link = links[_k];
this.reattachStylesheetLink(link);
}
}
return true;
};
Reloader.prototype.collectImportedStylesheets = function(link, styleSheet, result) {
var index, rule, rules, _len;
try {
rules = styleSheet != null ? styleSheet.cssRules : void 0;
} catch (e) {
}
if (rules && rules.length) {
for (index = 0, _len = rules.length; index < _len; index++) {
rule = rules[index];
switch (rule.type) {
case CSSRule.CHARSET_RULE:
continue;
case CSSRule.IMPORT_RULE:
result.push({
link: link,
rule: rule,
index: index,
href: rule.href
});
this.collectImportedStylesheets(link, rule.styleSheet, result);
break;
default:
break;
}
}
}
};
Reloader.prototype.reattachStylesheetLink = function(link) {
var clone, parent, timer;
if (link.__LiveReload_pendingRemoval) return;
link.__LiveReload_pendingRemoval = true;
clone = link.cloneNode(false);
clone.href = this.generateCacheBustUrl(link.href);
parent = link.parentNode;
if (parent.lastChild === link) {
parent.appendChild(clone);
} else {
parent.insertBefore(clone, link.nextSibling);
}
timer = new this.Timer(function() {
if (link.parentNode) return link.parentNode.removeChild(link);
});
return timer.start(this.stylesheetGracePeriod);
};
Reloader.prototype.reattachImportedRule = function(_arg) {
var href, index, link, media, newRule, parent, rule, tempLink;
var _this = this;
rule = _arg.rule, index = _arg.index, link = _arg.link;
parent = rule.parentStyleSheet;
href = this.generateCacheBustUrl(rule.href);
media = rule.media.length ? [].join.call(rule.media, ', ') : '';
newRule = "@import url(\"" + href + "\") " + media + ";";
rule.__LiveReload_newHref = href;
tempLink = this.document.createElement("link");
tempLink.rel = 'stylesheet';
tempLink.href = href;
tempLink.__LiveReload_pendingRemoval = true;
if (link.parentNode) link.parentNode.insertBefore(tempLink, link);
return this.Timer.start(this.importCacheWaitPeriod, function() {
if (tempLink.parentNode) tempLink.parentNode.removeChild(tempLink);
if (rule.__LiveReload_newHref !== href) return;
parent.insertRule(newRule, index);
parent.deleteRule(index + 1);
rule = parent.cssRules[index];
rule.__LiveReload_newHref = href;
return _this.Timer.start(_this.importCacheWaitPeriod, function() {
if (rule.__LiveReload_newHref !== href) return;
parent.insertRule(newRule, index);
return parent.deleteRule(index + 1);
});
});
};
Reloader.prototype.generateUniqueString = function() {
return 'livereload=' + Date.now();
};
Reloader.prototype.generateCacheBustUrl = function(url, expando) {
var hash, oldParams, params, _ref;
if (expando == null) expando = this.generateUniqueString();
_ref = splitUrl(url), url = _ref.url, hash = _ref.hash, oldParams = _ref.params;
if (this.options.overrideURL) {
if (url.indexOf(this.options.serverURL) < 0) {
url = this.options.serverURL + this.options.overrideURL + "?url=" + encodeURIComponent(url);
}
}
params = oldParams.replace(/(\?|&)livereload=(\d+)/, function(match, sep) {
return "" + sep + expando;
});
if (params === oldParams) {
if (oldParams.length === 0) {
params = "?" + expando;
} else {
params = "" + oldParams + "&" + expando;
}
}
return url + params + hash;
};
return Reloader;
})();
// livereload
var Connector, LiveReload, Options, Reloader, Timer;
Connector = __connector.Connector;
Timer = __timer.Timer;
Options = __options.Options;
Reloader = __reloader.Reloader;
__livereload.LiveReload = LiveReload = (function() {
function LiveReload(window) {
var _this = this;
this.window = window;
this.listeners = {};
this.plugins = [];
this.pluginIdentifiers = {};
this.console = this.window.location.href.match(/LR-verbose/) && this.window.console && this.window.console.log && this.window.console.error ? this.window.console : {
log: function() {},
error: function() {}
};
if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) {
console.error("LiveReload disabled because the browser does not seem to support web sockets");
return;
}
if (!(this.options = Options.extract(this.window.document))) {
console.error("LiveReload disabled because it could not find its own <SCRIPT> tag");
return;
}
this.reloader = new Reloader(this.window, this.console, Timer);
this.connector = new Connector(this.options, this.WebSocket, Timer, {
connecting: function() {},
socketConnected: function() {},
connected: function(protocol) {
var _base;
if (typeof (_base = _this.listeners).connect === "function") {
_base.connect();
}
_this.log("LiveReload is connected to " + _this.options.host + ":" + _this.options.port + " (protocol v" + protocol + ").");
return _this.analyze();
},
error: function(e) {
if (e instanceof ProtocolError) {
return console.log("" + e.message + ".");
} else {
return console.log("LiveReload internal error: " + e.message);
}
},
disconnected: function(reason, nextDelay) {
var _base;
if (typeof (_base = _this.listeners).disconnect === "function") {
_base.disconnect();
}
switch (reason) {
case 'cannot-connect':
return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + ", will retry in " + nextDelay + " sec.");
case 'broken':
return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + ", reconnecting in " + nextDelay + " sec.");
case 'handshake-timeout':
return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake timeout), will retry in " + nextDelay + " sec.");
case 'handshake-failed':
return _this.log("LiveReload cannot connect to " + _this.options.host + ":" + _this.options.port + " (handshake failed), will retry in " + nextDelay + " sec.");
case 'manual':
break;
case 'error':
break;
default:
return _this.log("LiveReload disconnected from " + _this.options.host + ":" + _this.options.port + " (" + reason + "), reconnecting in " + nextDelay + " sec.");
}
},
message: function(message) {
switch (message.command) {
case 'reload':
return _this.performReload(message);
case 'alert':
return _this.performAlert(message);
}
}
});
}
LiveReload.prototype.on = function(eventName, handler) {
return this.listeners[eventName] = handler;
};
LiveReload.prototype.log = function(message) {
return this.console.log("" + message);
};
LiveReload.prototype.performReload = function(message) {
var _ref, _ref2;
this.log("LiveReload received reload request for " + message.path + ".");
return this.reloader.reload(message.path, {
liveCSS: (_ref = message.liveCSS) != null ? _ref : true,
liveImg: (_ref2 = message.liveImg) != null ? _ref2 : true,
originalPath: message.originalPath || '',
overrideURL: message.overrideURL || '',
serverURL: "http://" + this.options.host + ":" + this.options.port
});
};
LiveReload.prototype.performAlert = function(message) {
return alert(message.message);
};
LiveReload.prototype.shutDown = function() {
var _base;
this.connector.disconnect();
this.log("LiveReload disconnected.");
return typeof (_base = this.listeners).shutdown === "function" ? _base.shutdown() : void 0;
};
LiveReload.prototype.hasPlugin = function(identifier) {
return !!this.pluginIdentifiers[identifier];
};
LiveReload.prototype.addPlugin = function(pluginClass) {
var plugin;
var _this = this;
if (this.hasPlugin(pluginClass.identifier)) return;
this.pluginIdentifiers[pluginClass.identifier] = true;
plugin = new pluginClass(this.window, {
_livereload: this,
_reloader: this.reloader,
_connector: this.connector,
console: this.console,
Timer: Timer,
generateCacheBustUrl: function(url) {
return _this.reloader.generateCacheBustUrl(url);
}
});
this.plugins.push(plugin);
this.reloader.addPlugin(plugin);
};
LiveReload.prototype.analyze = function() {
var plugin, pluginData, pluginsData, _i, _len, _ref;
if (!(this.connector.protocol >= 7)) return;
pluginsData = {};
_ref = this.plugins;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
plugin = _ref[_i];
pluginsData[plugin.constructor.identifier] = pluginData = (typeof plugin.analyze === "function" ? plugin.analyze() : void 0) || {};
pluginData.version = plugin.constructor.version;
}
this.connector.sendCommand({
command: 'info',
plugins: pluginsData,
url: this.window.location.href
});
};
return LiveReload;
})();
// less
var LessPlugin;
__less = LessPlugin = (function() {
LessPlugin.identifier = 'less';
LessPlugin.version = '1.0';
function LessPlugin(window, host) {
this.window = window;
this.host = host;
}
LessPlugin.prototype.reload = function(path, options) {
if (this.window.less && this.window.less.refresh) {
if (path.match(/\.less$/i)) {
return this.reloadLess(path);
}
if (options.originalPath.match(/\.less$/i)) {
return this.reloadLess(options.originalPath);
}
}
return false;
};
LessPlugin.prototype.reloadLess = function(path) {
var link, links, _i, _len;
links = (function() {
var _i, _len, _ref, _results;
_ref = document.getElementsByTagName('link');
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
link = _ref[_i];
if (link.href && link.rel === 'stylesheet/less' || (link.rel.match(/stylesheet/) && link.type.match(/^text\/(x-)?less$/))) {
_results.push(link);
}
}
return _results;
})();
if (links.length === 0) {
return false;
}
for (_i = 0, _len = links.length; _i < _len; _i++) {
link = links[_i];
link.href = this.host.generateCacheBustUrl(link.href);
}
this.host.console.log("LiveReload is asking LESS to recompile all stylesheets");
this.window.less.refresh(true);
return true;
};
LessPlugin.prototype.analyze = function() {
return {
disable: !!(this.window.less && this.window.less.refresh)
};
};
return LessPlugin;
})();
// startup
var CustomEvents, LiveReload, k;
CustomEvents = __customevents;
LiveReload = window.LiveReload = new (__livereload.LiveReload)(window);
for (k in window) {
if (k.match(/^LiveReloadPlugin/)) {
LiveReload.addPlugin(window[k]);
}
}
LiveReload.addPlugin(__less);
LiveReload.on('shutdown', function() {
return delete window.LiveReload;
});
LiveReload.on('connect', function() {
return CustomEvents.fire(document, 'LiveReloadConnect');
});
LiveReload.on('disconnect', function() {
return CustomEvents.fire(document, 'LiveReloadDisconnect');
});
CustomEvents.bind(document, 'LiveReloadShutDown', function() {
return LiveReload.shutDown();
});
})();

View File

@@ -0,0 +1,436 @@
import sublime,sublime_plugin,threading,json,os,threading,subprocess,socket,shutil
from base64 import b64encode, b64decode
# python 2.6 differences
try:
from hashlib import md5, sha1
except: from md5 import md5; from sha import sha as sha1
from SimpleHTTPServer import SimpleHTTPRequestHandler
from struct import pack, unpack_from
import array, struct, os
import urllib2
s2a = lambda s: [ord(c) for c in s]
settings = sublime.load_settings('LiveReload.sublime-settings')
port = settings.get('port')
version = settings.get('version')
##LOAD latest livereload.js from github (for v2 of protocol) or if this fails local version
try:
req = urllib2.urlopen(urllib2.Request("http://raw.github.com/livereload/livereload-js/master/dist/livereload.js"))
source_livereload_js = req.read()
if not "http://livereload.com/protocols/official-6" in source_livereload_js:
raise Exception("Something wrong with download!")
except Exception, u:
print u
try:
path = os.path.join(sublime.packages_path(), "LiveReload")
local = open(os.path.join(path, "livereload.js"), "rU")
source_livereload_js = local.read()
except IOError, e:
print e
sublime.error_message("livereload.js is missing from LiveReload package install")
class LiveReload(threading.Thread):
def run(self):
global LivereloadFactory
threading.Thread.__init__(self)
LivereloadFactory = WebSocketServer(port,version)
LivereloadFactory.start()
class LiveReloadChange(sublime_plugin.EventListener):
def __init__ (self):
LiveReload().start()
def __del__(self):
global LivereloadFactory
LivereloadFactory.stop()
def on_post_save(self, view):
global LivereloadFactory
settings = sublime.load_settings('LiveReload.sublime-settings')
filename = view.file_name()
if view.file_name().find('.scss') > 0 or view.file_name().find('.sass') > 0:
compiler = CompassThread(filename,LivereloadFactory)
compiler.start()
else:
filename = os.path.normcase(filename)
filename = os.path.split(filename)[1]
filename = filename.replace('.scss','.css').replace('.styl','.css').replace('.less','.css')
filename = filename.replace('.coffee','.js')
data = json.dumps(["refresh", {
"path": filename,
"apply_js_live": settings.get('apply_js_live'),
"apply_css_live": settings.get('apply_css_live'),
"apply_images_live": settings.get('apply_images_live')
}])
sublime.set_timeout(lambda: LivereloadFactory.send_all(data), int(settings.get('delay_ms')))
sublime.set_timeout(lambda: sublime.status_message("Sent LiveReload command for file: "+filename), int(settings.get('delay_ms')))
class CompassThread(threading.Thread):
def __init__(self, filename,LivereloadFactory):
self.dirname = os.path.dirname(filename)
self.filename = filename.replace('.scss','.css').replace('.sass','.css')
self.LivereloadFactory = LivereloadFactory
self.stdout = None
self.stderr = None
threading.Thread.__init__(self)
def run(self):
global LivereloadFactory
print 'compass compile ' + self.dirname
# autocreate config.rb for compass
if not os.path.exists(os.path.join(self.dirname, "config.rb")):
print "Generating config.rb"
shutil.copy(os.path.join(sublime.packages_path(), "LiveReload","assets","config.rb"), self.dirname)
# compass compile
p = subprocess.Popen(['compass compile ' + self.dirname.replace('\\','/')],shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
if p.stdout.read() :
self.LivereloadFactory.send_all(json.dumps(["refresh", {
"path": self.filename.replace('\\','/'),
"apply_js_live": True,
"apply_css_live": True,
"apply_images_live": True
}]))
class WebSocketServer:
"""
Handle the Server, bind and accept new connections, open and close
clients connections.
"""
def __init__(self, port, version):
self.clients = []
self.port = port
self.version = version
self.s = None
def stop(self):
[client.close() for client in self.clients]
l = threading.Lock()
l.acquire()
l.release()
def start(self):
"""
Start the server.
"""
try:
self.s = socket.socket()
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.s.bind(('', self.port))
self.s.listen(1)
except Exception, e:
self.stop()
try:
while 1:
conn, addr = self.s.accept()
newClient = WebSocketClient(conn, addr, self)
self.clients.append(newClient)
newClient.start()
except Exception, e:
self.stop()
def send_all(self, data):
"""
Send a message to all the currenly connected clients.
"""
[client.send(data) for client in self.clients]
def remove(self, client):
"""
Remove a client from the connected list.
"""
try:
l = threading.Lock()
l.acquire()
self.clients.remove(client)
l.release()
except Exception, e:
pass
class WebSocketClient(threading.Thread):
"""
A single connection (client) of the program
"""
# Handshaking, create the WebSocket connection
server_handshake_hybi = """HTTP/1.1 101 Switching Protocols\r
Upgrade: websocket\r
Connection: Upgrade\r
Sec-WebSocket-Accept: %s\r
"""
GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
def __init__(self, sock, addr, server):
threading.Thread.__init__(self)
self.s = sock
self.addr = addr
self.server = server
def run(self):
wsh = WSRequestHandler(self.s, self.addr, False)
h = self.headers = wsh.headers
ver = h.get('Sec-WebSocket-Version')
if ver:
# HyBi/IETF version of the protocol
# HyBi-07 report version 7
# HyBi-08 - HyBi-12 report version 8
# HyBi-13 reports version 13
if ver in ['7', '8', '13']:
self.version = "hybi-%02d" % int(ver)
else:
raise Exception('Unsupported protocol version %s' % ver)
key = h['Sec-WebSocket-Key']
print key
# Generate the hash value for the accept header
accept = b64encode(sha1(key + self.GUID).digest())
response = self.server_handshake_hybi % accept
response += "\r\n"
print response
self.s.send(response.encode())
self.new_client()
# Receive and handle data
while 1:
try:
data = self.s.recv(1024)
except Exception, e:
break
if not data: break
dec = WebSocketClient.decode_hybi(data)
if dec["opcode"] == 8:
self.close()
else:
self.onreceive(dec)
# Close the client connection
self.close()
@staticmethod
def unmask(buf, f):
pstart = f['hlen'] + 4
pend = pstart + f['length']
# Slower fallback
data = array.array('B')
mask = s2a(f['mask'])
data.fromstring(buf[pstart:pend])
for i in range(len(data)):
data[i] ^= mask[i % 4]
return data.tostring()
@staticmethod
def encode_hybi(buf, opcode, base64=False):
""" Encode a HyBi style WebSocket frame.
Optional opcode:
0x0 - continuation
0x1 - text frame (base64 encode buf)
0x2 - binary frame (use raw buf)
0x8 - connection close
0x9 - ping
0xA - pong
"""
if base64:
buf = b64encode(buf)
b1 = 0x80 | (opcode & 0x0f) # FIN + opcode
payload_len = len(buf)
if payload_len <= 125:
header = pack('>BB', b1, payload_len)
elif payload_len > 125 and payload_len < 65536:
header = pack('>BBH', b1, 126, payload_len)
elif payload_len >= 65536:
header = pack('>BBQ', b1, 127, payload_len)
print("Encoded: %s" % repr(header + buf))
return header + buf, len(header), 0
@staticmethod
def decode_hybi(buf, base64=False):
""" Decode HyBi style WebSocket packets.
Returns:
{'fin' : 0_or_1,
'opcode' : number,
'mask' : 32_bit_number,
'hlen' : header_bytes_number,
'length' : payload_bytes_number,
'payload' : decoded_buffer,
'left' : bytes_left_number,
'close_code' : number,
'close_reason' : string}
"""
f = {'fin' : 0,
'opcode' : 0,
'mask' : 0,
'hlen' : 2,
'length' : 0,
'payload' : None,
'left' : 0,
'close_code' : None,
'close_reason' : None}
blen = len(buf)
f['left'] = blen
if blen < f['hlen']:
return f # Incomplete frame header
b1, b2 = unpack_from(">BB", buf)
f['opcode'] = b1 & 0x0f
f['fin'] = (b1 & 0x80) >> 7
has_mask = (b2 & 0x80) >> 7
f['length'] = b2 & 0x7f
if f['length'] == 126:
f['hlen'] = 4
if blen < f['hlen']:
return f # Incomplete frame header
(f['length'],) = unpack_from('>xxH', buf)
elif f['length'] == 127:
f['hlen'] = 10
if blen < f['hlen']:
return f # Incomplete frame header
(f['length'],) = unpack_from('>xxQ', buf)
full_len = f['hlen'] + has_mask * 4 + f['length']
if blen < full_len: # Incomplete frame
return f # Incomplete frame header
# Number of bytes that are part of the next frame(s)
f['left'] = blen - full_len
# Process 1 frame
if has_mask:
# unmask payload
f['mask'] = buf[f['hlen']:f['hlen']+4]
f['payload'] = WebSocketClient.unmask(buf, f)
else:
print("Unmasked frame: %s" % repr(buf))
f['payload'] = buf[(f['hlen'] + has_mask * 4):full_len]
if base64 and f['opcode'] in [1, 2]:
try:
f['payload'] = b64decode(f['payload'])
except:
print("Exception while b64decoding buffer: %s" %
repr(buf))
raise
if f['opcode'] == 0x08:
if f['length'] >= 2:
f['close_code'] = unpack_from(">H", f['payload'])
if f['length'] > 3:
f['close_reason'] = f['payload'][2:]
return f
def close(self):
"""
Close this connection
"""
self.server.remove(self)
self.s.close()
def send(self, msg):
"""
Send a message to this client
"""
msg = WebSocketClient.encode_hybi(msg, 0x1, False)
self.s.send(msg[0])
def onreceive(self, data):
"""
Event called when a message is received from this client
"""
try:
print data
if "payload" in data:
print "payload true"
if "hello" in data.get("payload"):
sublime.set_timeout(lambda: sublime.status_message("New LiveReload v2 client connected"), 100)
self.send('{"command":"hello","protocols":["http://livereload.com/protocols/connection-check-1","http://livereload.com/protocols/official-6"]}')
else:
sublime.set_timeout(lambda: sublime.status_message("New LiveReload v1 client connected"), 100)
self.send("!!ver:" + str(self.server.version))
except Exception, e:
print e
def new_client(self):
"""
Event called when handshake is compleated
"""
#self.send("!!ver:"+str(self.server.version))
def _clean(self, msg):
"""
Remove special chars used for the transmission
"""
msg = msg.replace(b'\x00', b'', 1)
msg = msg.replace(b'\xff', b'', 1)
return msg
# HTTP handler with WebSocket upgrade support
class WSRequestHandler(SimpleHTTPRequestHandler):
def __init__(self, req, addr, only_upgrade=True):
self.only_upgrade = only_upgrade # only allow upgrades
SimpleHTTPRequestHandler.__init__(self, req, addr, object())
def do_GET(self):
if (self.headers.get('upgrade') and
self.headers.get('upgrade').lower() == 'websocket'):
if (self.headers.get('sec-websocket-key1') or
self.headers.get('websocket-key1')):
# For Hixie-76 read out the key hash
self.headers.__setitem__('key3', self.rfile.read(8))
# Just indicate that an WebSocket upgrade is needed
self.last_code = 101
self.last_message = "101 Switching Protocols"
elif self.only_upgrade:
# Normal web request responses are disabled
self.last_code = 405
self.last_message = "405 Method Not Allowed"
else:
#Self injecting plugin
if "livereload.js" in self.path:
self.send_response(200, 'OK')
self.send_header('Content-type', 'text/javascript')
self.send_header("Content-Length", len(source_livereload_js))
self.end_headers()
self.wfile.write(bytes(source_livereload_js))
return
else:
#Disable other requests
self.send_response(405, 'Method Not Allowed')
self.send_header('Content-type', 'text/plain')
self.send_header("Content-Length", len("Method Not Allowed"))
self.end_headers()
self.wfile.write(bytes("Method Not Allowed"))
return
def send_response(self, code, message=None):
# Save the status code
self.last_code = code
SimpleHTTPRequestHandler.send_response(self, code, message)
def log_message(self, f, *args):
# Save instead of printing
self.last_message = f % args

View File

@@ -0,0 +1,4 @@
{
"install": "messages/install_upgrade.txt",
"upgrade": "messages/install_upgrade.txt"
}

View File

@@ -0,0 +1,9 @@
Changelog:
Please be sure to restart Sublime Text 2 to start using this new version. For more information or how to use this plugin visit https://github.com/dz0ny/LiveReload-sublimetext2/blob/master/README.md
New Features
- Support v2 of protocol
Bugs:
- Potential security vulnerability with plugin https://github.com/dz0ny/LiveReload-sublimetext2/issues/7

View File

@@ -0,0 +1 @@
{"url": "https://github.com/dz0ny/LiveReload-sublimetext2", "version": "2013.03.07.03.08.21", "description": "LiveReload plugin for SublimeText"}