зеркало из https://github.com/mozilla/brackets.git
Fixed #177 - Land brackets-browser-livedev in the tree
This commit is contained in:
Родитель
cd3b83ad6d
Коммит
62a3063635
|
@ -28,9 +28,6 @@
|
|||
[submodule "src/thirdparty/filer"]
|
||||
path = src/thirdparty/filer
|
||||
url = https://github.com/filerjs/filer.git
|
||||
[submodule "src/extensions/default/brackets-browser-livedev"]
|
||||
path = src/extensions/default/brackets-browser-livedev
|
||||
url = https://github.com/humphd/brackets-browser-livedev.git
|
||||
[submodule "src/extensions/default/brackets-paste-and-indent"]
|
||||
path = src/extensions/default/brackets-paste-and-indent
|
||||
url = https://github.com/ahuth/brackets-paste-and-indent.git
|
||||
|
|
12
Gruntfile.js
12
Gruntfile.js
|
@ -363,10 +363,10 @@ module.exports = function (grunt) {
|
|||
// These modules include lots of third-party code, so we skip them
|
||||
'!src/extensions/default/HTMLHinter/slowparse/**',
|
||||
'!src/extensions/default/HTMLHinter/tooltipsy.source.js',
|
||||
'!src/extensions/default/brackets-browser-livedev/nohost/**',
|
||||
'!src/extensions/default/bramble/nohost/**',
|
||||
'!src/extensions/extra/**',
|
||||
//With Previous skip statement, this file was ignored, so we specify it directly for jshinting
|
||||
'src/extensions/default/brackets-browser-livedev/nohost/src/NoHostServer.js'
|
||||
'src/extensions/default/bramble/nohost/src/NoHostServer.js'
|
||||
],
|
||||
grunt: '<%= meta.grunt %>',
|
||||
src: [
|
||||
|
@ -374,10 +374,10 @@ module.exports = function (grunt) {
|
|||
// These modules include lots of third-party code, so we skip them
|
||||
'!src/extensions/default/HTMLHinter/slowparse/**',
|
||||
'!src/extensions/default/HTMLHinter/tooltipsy.source.js',
|
||||
'!src/extensions/default/brackets-browser-livedev/nohost/**',
|
||||
'!src/extensions/default/bramble/nohost/**',
|
||||
'!src/extensions/extra/**',
|
||||
//With Previous skip statement, this file was ignored, so we specify it directly for jshinting
|
||||
'src/extensions/default/brackets-browser-livedev/nohost/src/NoHostServer.js'
|
||||
'src/extensions/default/bramble/nohost/src/NoHostServer.js'
|
||||
],
|
||||
test: '<%= meta.test %>',
|
||||
/* use strict options to mimic JSLINT until we migrate to JSHINT in Brackets */
|
||||
|
@ -406,7 +406,7 @@ module.exports = function (grunt) {
|
|||
"update_submodules": {
|
||||
publish: {
|
||||
options: {
|
||||
params: "--remote -- src/extensions/default/brackets-browser-livedev src/extensions/default/HTMLHinter"
|
||||
params: "--remote -- src/extensions/default/bramble src/extensions/default/HTMLHinter"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -436,7 +436,7 @@ module.exports = function (grunt) {
|
|||
},
|
||||
modules: {
|
||||
files: {
|
||||
src: ['./src/extensions/default/brackets-browser-livedev', './src/extensions/default/HTMLHinter']
|
||||
src: ['./src/extensions/default/bramble', './src/extensions/default/HTMLHinter']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 1d4fbe6fdceba729163409f21c53a06020fbd1ff
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 David Humphrey
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# brackets-browser-livedev
|
||||
In-Browser Live Development Extension for Brackets
|
|
@ -0,0 +1,9 @@
|
|||
<div class="phone-container">
|
||||
<div class="phone-inner">
|
||||
<div class="phone-top"></div>
|
||||
<div id="phone-content" class="phone-border">
|
||||
</div>
|
||||
<div class="phone-shadow"></div>
|
||||
<div class="phone-bottom"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,174 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
|
||||
/*global define, brackets*/
|
||||
|
||||
// This transport provides a PostMessage connection between Brackets and a live browser preview.
|
||||
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var _iframeRef,
|
||||
connId = 1;
|
||||
|
||||
var Launcher = require("lib/launcher"),
|
||||
Browser = require("lib/iframe-browser");
|
||||
|
||||
var EventDispatcher = brackets.getModule("utils/EventDispatcher"),
|
||||
LiveDevMultiBrowser = brackets.getModule("LiveDevelopment/LiveDevMultiBrowser"),
|
||||
BlobUtils = brackets.getModule("filesystem/impls/filer/BlobUtils"),
|
||||
Path = brackets.getModule("filesystem/impls/filer/BracketsFiler").Path;
|
||||
|
||||
// The script that will be injected into the previewed HTML to handle the other side of the post message connection.
|
||||
var PostMessageTransportRemote = require("text!lib/PostMessageTransportRemote.js");
|
||||
|
||||
// An XHR shim will be injected as well to allow XHR to the file system
|
||||
var XHRShim = require("text!lib/xhr/XHRShim.js");
|
||||
|
||||
EventDispatcher.makeEventDispatcher(module.exports);
|
||||
|
||||
/**
|
||||
* Saves a reference of the iframe element to a local variable
|
||||
* @param {DOM element reference to an iframe}
|
||||
*/
|
||||
function setIframe(iframeRef) {
|
||||
if(iframeRef) {
|
||||
_iframeRef = iframeRef;
|
||||
}
|
||||
}
|
||||
|
||||
// This function maps all blob urls in a message to filesystem
|
||||
// paths based on the urls that are cached, so that Brackets can work
|
||||
// with paths vs. urls
|
||||
// For e.g. a message like `{"stylesheets": {"blob://http://url" :
|
||||
// ["blob://http://url"]}}` will be mapped into `{"stylesheets":
|
||||
// {"/file1" : ["/file1"]}}`
|
||||
function resolveLinks(message) {
|
||||
var regex = new RegExp('\\"(blob:[^"]+)\\"', 'gm');
|
||||
var resolvedMessage = message.replace(regex, function(match, url) {
|
||||
var path = BlobUtils.getFilename(url);
|
||||
|
||||
return ["\"", path, "\""].join("");
|
||||
});
|
||||
|
||||
return resolvedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message event listener
|
||||
*/
|
||||
function _listener(event){
|
||||
var msgObj;
|
||||
|
||||
try {
|
||||
msgObj = JSON.parse(event.data);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(msgObj.type === "message"){
|
||||
if(msgObj.message) {
|
||||
msgObj.message = resolveLinks(msgObj.message);
|
||||
}
|
||||
|
||||
//trigger message event
|
||||
module.exports.trigger("message", [connId, msgObj.message]);
|
||||
} else if (msgObj.type === "connect") {
|
||||
Browser.setListener();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the post message connection
|
||||
*/
|
||||
function start(){
|
||||
window.addEventListener("message", _listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces paths of linked files with blob urls
|
||||
* @param {string} message - Contains a message passed by the editor
|
||||
* to the LiveDev preview. Some examples of these messages are:
|
||||
* "{"method":"Runtime.evaluate","params":{"expression":"_LD.highlightRule(\"[data-brackets-id='6']\")"},"id":3}"
|
||||
* "{"method":"Runtime.evaluate","params":{"expression":"_LD.applyDOMEdits([{\"type\":\"attrChange\",\"tagID\":13,\"attribute\":\"href\",\"value\":\"script.js\"}])"},"id":14}"
|
||||
* "{"method":"CSS.setStylesheetText","params":{"url":"/style.css","text":"/**\n * This is a basic CSS file. You can include and use it in\n * your web page by adding the following inside the <head>:\n * <link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\">\n * /\np {\n color: purpl;\n}\n"},"id":28}"
|
||||
* As can be noted in the second and third examples, there are cases
|
||||
* when a \ and/or " may or may not be present before the substring
|
||||
* that needs to be replaced
|
||||
*
|
||||
*/
|
||||
function resolvePaths(message) {
|
||||
var currentDoc = LiveDevMultiBrowser._getCurrentLiveDoc();
|
||||
if(!currentDoc) {
|
||||
return message;
|
||||
}
|
||||
|
||||
var currentDir = Path.dirname(currentDoc.doc.file.fullPath);
|
||||
var linkRegex = new RegExp('(\\\\?\\"?)(href|src|url|value)(\\\\?\\"?\\s?:?\\s?\\(?\\\\?\\"?)([^\\\\"\\),]+)(\\\\?\\"?)', 'gm');
|
||||
var resolvedMessage = message.replace(linkRegex, function(match, quote1, attr, seperator, path, quote2) {
|
||||
path = BlobUtils.getUrl(path.charAt(0) === "/" ? path : Path.join(currentDir, path));
|
||||
|
||||
return [quote1, attr, seperator, path, quote2].join('');
|
||||
});
|
||||
|
||||
return resolvedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a transport-layer message via post message.
|
||||
* @param {number|Array.<number>} idOrArray A client ID or array of client IDs to send the message to.
|
||||
* @param {string} msgStr The message to send as a JSON string.
|
||||
*/
|
||||
function send(idOrArray, msgStr){
|
||||
var win = _iframeRef.contentWindow;
|
||||
msgStr = resolvePaths(msgStr);
|
||||
var msg = JSON.parse(msgStr);
|
||||
var detachedPreview = Browser.getDetachedPreview();
|
||||
|
||||
// Because we need to deal with reloads on this side (i.e., editor) of the
|
||||
// transport, check message before sending to remote, and reload if necessary
|
||||
// without actually sending to remote for processing.
|
||||
if(msg.method === "Page.reload" || msg.method === "Page.navigate") {
|
||||
reload();
|
||||
return;
|
||||
}
|
||||
|
||||
win.postMessage(msgStr, "*");
|
||||
|
||||
if(detachedPreview) {
|
||||
detachedPreview.postMessage(msgStr, "*");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the message event listener
|
||||
*/
|
||||
function close(clientId){
|
||||
window.removeEventListener("message", _listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the script that should be injected into the browser to handle the other end of the transport.
|
||||
* Includes a base tag to handle external protocol-less, linked files.
|
||||
* @return {string}
|
||||
*/
|
||||
function getRemoteScript() {
|
||||
return '<base href="' + window.location.href + '">\n' +
|
||||
"<script>\n" + PostMessageTransportRemote + "</script>\n" +
|
||||
"<script>\n" + XHRShim + "</script>\n";
|
||||
}
|
||||
|
||||
function reload() {
|
||||
var launcher = Launcher.getCurrentInstance();
|
||||
var liveDoc = LiveDevMultiBrowser._getCurrentLiveDoc();
|
||||
var url = BlobUtils.getUrl(liveDoc.doc.file.fullPath);
|
||||
|
||||
launcher.launch(url);
|
||||
}
|
||||
|
||||
// Exports
|
||||
module.exports.getRemoteScript = getRemoteScript;
|
||||
module.exports.setIframe = setIframe;
|
||||
module.exports.start = start;
|
||||
module.exports.send = send;
|
||||
module.exports.close = close;
|
||||
module.exports.reload = reload;
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
// This is a transport injected into the browser via a script that handles the low
|
||||
// level communication between the live development protocol handlers on both sides.
|
||||
// This transport provides a postMessage mechanism. It's injected separately from the
|
||||
// protocol handler so that the transport can be changed separately.
|
||||
|
||||
(function (global) {
|
||||
"use strict";
|
||||
// In case of detached preview window.opener will refer to editor window
|
||||
var parent = window.opener || window.parent;
|
||||
var postMessageTransport = {
|
||||
/**
|
||||
* @private
|
||||
* An object that contains callbacks to handle various transport events. See `setCallbacks()`.
|
||||
* @type {?{connect: ?function, message: ?function(string), close: ?function}}
|
||||
*/
|
||||
_callbacks: null,
|
||||
|
||||
/**
|
||||
* Sets the callbacks that should be called when various transport events occur.
|
||||
*/
|
||||
setCallbacks: function (callbacks) {
|
||||
this._callbacks = callbacks;
|
||||
},
|
||||
|
||||
connect: function (url) {
|
||||
var self = this;
|
||||
|
||||
parent.postMessage(JSON.stringify({
|
||||
type: "connect",
|
||||
url: global.location.href
|
||||
}), "*");
|
||||
|
||||
if (self._callbacks && self._callbacks.connect) {
|
||||
self._callbacks.connect();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message over the transport.
|
||||
* @param {string} msgStr The message to send.
|
||||
*/
|
||||
send: function (msgStr) {
|
||||
parent.postMessage(JSON.stringify({
|
||||
type: "message",
|
||||
message: msgStr
|
||||
}), "*");
|
||||
},
|
||||
|
||||
/**
|
||||
* Establish postMessage connection.
|
||||
*/
|
||||
enable: function () {
|
||||
this.connect();
|
||||
var self = this;
|
||||
|
||||
// Listen for message.
|
||||
window.addEventListener("message", function (event) {
|
||||
if (self._callbacks && self._callbacks.message) {
|
||||
self._callbacks.message(event.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
global._Brackets_LiveDev_Transport = postMessageTransport;
|
||||
}(this));
|
|
@ -0,0 +1,218 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
|
||||
/*global define, brackets: true, $*/
|
||||
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var Menus = brackets.getModule("command/Menus"),
|
||||
Resizer = brackets.getModule("utils/Resizer"),
|
||||
UrlParams = brackets.getModule("utils/UrlParams").UrlParams,
|
||||
StatusBar = brackets.getModule("widgets/StatusBar"),
|
||||
Strings = brackets.getModule("strings"),
|
||||
MainViewManager = brackets.getModule("view/MainViewManager");
|
||||
|
||||
var PhonePreview = require("text!lib/Mobile.html");
|
||||
var PostMessageTransport = require("lib/PostMessageTransport");
|
||||
var IframeBrowser = require("lib/iframe-browser");
|
||||
var Compatibility = require("lib/compatibility");
|
||||
|
||||
/**
|
||||
* This function calls all the hide functions and listens
|
||||
* for bramble to be loaded
|
||||
*/
|
||||
function initUI(callback) {
|
||||
addLivePreviewButton(function() {
|
||||
toggleMobileViewButton();
|
||||
|
||||
if(shouldHideUI()) {
|
||||
removeTitleBar();
|
||||
removeMainToolBar();
|
||||
removeLeftSideToolBar();
|
||||
removeRightSideToolBar();
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function parses brackets URL, and looks for the GET parameter "ui"
|
||||
* if ui is set to 1, then the UI is shown
|
||||
*/
|
||||
function shouldHideUI() {
|
||||
var params = new UrlParams();
|
||||
params.parse();
|
||||
return params.get("ui") !== "1";
|
||||
}
|
||||
|
||||
/**
|
||||
* This function merely removes the left side tool bar
|
||||
*/
|
||||
function removeLeftSideToolBar() {
|
||||
//Hide second pane working set list
|
||||
$("#working-set-list-second-pane").addClass("hideLeftToolbar");
|
||||
//Remove splitview button
|
||||
$("#sidebar .working-set-splitview-btn").remove();
|
||||
Resizer.hide("#sidebar");
|
||||
}
|
||||
|
||||
/**
|
||||
* This function merely removes the title bar
|
||||
* and the header of the first pane
|
||||
*/
|
||||
function removeTitleBar() {
|
||||
$("#titlebar").remove();
|
||||
$("#first-pane .pane-header").remove();
|
||||
//Alter the height of the affected elements
|
||||
$("#editor-holder").addClass("editor-holder-height");
|
||||
$("#first-pane .pane-content, .cm-s-light-theme").addClass("first-pane-height");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to remove the top tool bar
|
||||
*/
|
||||
function removeMainToolBar() {
|
||||
//remove the file menu
|
||||
Menus.removeMenu(Menus.AppMenuBar.FILE_MENU);
|
||||
|
||||
//remove the edit menu
|
||||
Menus.removeMenu(Menus.AppMenuBar.EDIT_MENU);
|
||||
|
||||
//remove the find menu
|
||||
Menus.removeMenu(Menus.AppMenuBar.FIND_MENU);
|
||||
|
||||
//remove the view menu
|
||||
Menus.removeMenu(Menus.AppMenuBar.VIEW_MENU);
|
||||
|
||||
//remove the navigate menu
|
||||
Menus.removeMenu(Menus.AppMenuBar.NAVIGATE_MENU);
|
||||
|
||||
//remove the help menu
|
||||
Menus.removeMenu(Menus.AppMenuBar.HELP_MENU);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to remove the right side tool bar
|
||||
*/
|
||||
function removeRightSideToolBar() {
|
||||
Resizer.makeResizable("#main-toolbar");
|
||||
Resizer.hide("#main-toolbar");
|
||||
$(".content").addClass("hideRightToolbar");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to show the mobile view.
|
||||
*/
|
||||
function showMobile() {
|
||||
$("#bramble-iframe-browser").addClass("phone-body");
|
||||
$("#second-pane").append(PhonePreview);
|
||||
$("#bramble-iframe-browser").appendTo("#phone-content");
|
||||
$("#second-pane").addClass("second-pane-scroll");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to hide the mobile view.
|
||||
*/
|
||||
function hideMobile() {
|
||||
$("#bramble-iframe-browser").appendTo("#second-pane");
|
||||
$(".phone-container").detach();
|
||||
$("#second-pane").removeClass("second-pane-scroll");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to add a button to the status bar to toggle
|
||||
* between mobile view and desktop view.
|
||||
*/
|
||||
function toggleMobileViewButton() {
|
||||
var isMobileViewOpen = false;
|
||||
|
||||
var mobileView = Mustache.render("<div><a id='mobileViewButton' href=#></a></div>", Strings);
|
||||
StatusBar.addIndicator("mobileViewButtonBox", $(mobileView), true, "",
|
||||
"Click to open preview in a mobile view", "status-overwrite");
|
||||
$("#mobileViewButton").addClass("mobileButton");
|
||||
|
||||
$("#mobileViewButton").click(function () {
|
||||
PostMessageTransport.reload();
|
||||
|
||||
if(!isMobileViewOpen) {
|
||||
// Switch the icon
|
||||
$("#mobileViewButton").removeClass("mobileButton");
|
||||
$("#mobileViewButton").addClass("desktopButton");
|
||||
// Updates the tooltip
|
||||
StatusBar.updateIndicator("mobileViewButtonBox", true, "",
|
||||
"Click to open preview in a desktop view");
|
||||
|
||||
showMobile();
|
||||
|
||||
isMobileViewOpen = true;
|
||||
// Give focus back to the editor when the outside of the mobile phone is clicked.
|
||||
// Prevents the status bar from disappearing.
|
||||
$("#second-pane").click(function() {
|
||||
MainViewManager.setActivePaneId("first-pane");
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Switch the icon
|
||||
$("#mobileViewButton").removeClass("desktopButton");
|
||||
$("#mobileViewButton").addClass("mobileButton");
|
||||
// Updates the tooltip
|
||||
StatusBar.updateIndicator("mobileViewButtonBox", true, "",
|
||||
"Click to open preview in a mobile view");
|
||||
|
||||
hideMobile();
|
||||
|
||||
isMobileViewOpen = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to add a button to the status bar for the
|
||||
* detached live preview.
|
||||
* For IE 11, we show the detach button, but only changes
|
||||
* to code that trigger a reload will show up in the detached
|
||||
* window.
|
||||
* For IE <11, we do not show the detach button
|
||||
*/
|
||||
function addLivePreviewButton(callback) {
|
||||
var livePreview = Mustache.render("<div><a id='liveDevButton' href=#></a></div>", Strings);
|
||||
StatusBar.addIndicator("liveDevButtonBox", $(livePreview), true, "",
|
||||
"Click to open preview in separate window", "mobileViewButtonBox");
|
||||
$("#liveDevButton").addClass("liveDevButtonDetach");
|
||||
|
||||
$("#liveDevButton").click(function () {
|
||||
Resizer.makeResizable("#second-pane");
|
||||
|
||||
// Checks if the attached preview is visible.
|
||||
// If it is, the attached preview is hidden
|
||||
// and the detached preview is opened.
|
||||
if(Resizer.isVisible("#second-pane")) {
|
||||
IframeBrowser.detachPreview();
|
||||
}
|
||||
else {
|
||||
IframeBrowser.attachPreview();
|
||||
}
|
||||
});
|
||||
|
||||
Compatibility.supportsIFrameHTMLBlobURL(function(err, isCompatible) {
|
||||
if(err) {
|
||||
console.error("[Brackets IFrame-Browser] Unexpected error:", err);
|
||||
return callback();
|
||||
}
|
||||
|
||||
// If we are in IE v<11, we hide the detachable preview button
|
||||
if(!isCompatible && document.all) {
|
||||
$("#liveDevButton").css("display", "none");
|
||||
console.log("[Brackets IFrame-Browser] Detachable preview disabled due to incompatibility with current browser (you are possibly running IE 10 or below)");
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// Define public API
|
||||
exports.initUI = initUI;
|
||||
exports.removeLeftSideToolBar = removeLeftSideToolBar;
|
||||
exports.removeMainToolBar = removeMainToolBar;
|
||||
exports.removeRightSideToolBar = removeRightSideToolBar;
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
|
||||
<path fill="#000000" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 19,57L 19,19L 57,19L 57,57L 19,57 Z M 24,29L 47,29L 47,52L 52,52L 52,24L 24,24L 24,29 Z M 24,34L 24,52L 42,52L 42,34L 24,34 Z "/>
|
||||
</svg>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
|
||||
<path fill="#000000" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 44.3333,19L 57,19L 57,31.6667L 52.25,36.4167L 52.25,27.7083L 34.8333,45.125L 30.875,41.1667L 48.2917,23.75L 39.5833,23.75L 44.3333,19 Z M 19,25.3333L 42.75,25.3333L 38,30.0833L 23.75,30.0833L 23.75,52.25L 45.9167,52.25L 45.9167,38L 50.6667,33.25L 50.6667,57L 19,57L 19,25.3333 Z "/>
|
||||
</svg>
|
|
@ -0,0 +1,59 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4,
|
||||
maxerr: 50, browser: true */
|
||||
/*global define, URL */
|
||||
|
||||
/**
|
||||
* Not all browsers support everything we need for the live preview (I'm
|
||||
* looking at you, IE). This code tests features we need, and provides
|
||||
* different strategies to solve those issues.
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Try to create a Blob URL with type text/html and put in an iframe.
|
||||
* IE won't allow it, so we have to use document.write(). The callback
|
||||
* returns true if Blob URLs of type text/html are supported.
|
||||
*/
|
||||
var _supportsIFrameHTMLBlobURL;
|
||||
|
||||
exports.supportsIFrameHTMLBlobURL = function(callback) {
|
||||
if(typeof _supportsIFrameHTMLBlobURL !== "undefined") {
|
||||
callback(null, _supportsIFrameHTMLBlobURL);
|
||||
return;
|
||||
}
|
||||
|
||||
var blob = new Blob(["<div id='bloburl-test'></div>"], {type: "text/html"});
|
||||
var blobURL = URL.createObjectURL(blob);
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.style.display = "none";
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
var onloadTimer;
|
||||
|
||||
function onload() {
|
||||
clearTimeout(onloadTimer);
|
||||
|
||||
// If the iframe doesn't have the expected content, it didn't load properly
|
||||
try {
|
||||
_supportsIFrameHTMLBlobURL = !!(iframe.contentWindow.document.querySelector("#bloburl-test"));
|
||||
document.body.removeChild(iframe);
|
||||
iframe = null;
|
||||
URL.revokeObjectURL(blobURL);
|
||||
blob = null;
|
||||
blobURL = null;
|
||||
} catch(e) {
|
||||
_supportsIFrameHTMLBlobURL = false;
|
||||
}
|
||||
|
||||
callback(null, _supportsIFrameHTMLBlobURL);
|
||||
}
|
||||
|
||||
iframe.onload = onload;
|
||||
iframe.src = blobURL;
|
||||
|
||||
// Just in case a browser doesn't fire onload for the iframe
|
||||
onloadTimer = setTimeout(onload, 1000);
|
||||
};
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* This is a basic JavaScript file. You can include
|
||||
* and use it in your web page by adding the following:
|
||||
* <script src="script.js"></script>
|
||||
*/
|
||||
function add(a, b) {
|
||||
return a + b;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* This is a basic CSS file. You can include and use it in
|
||||
* your web page by adding the following inside the <head>:
|
||||
* <link href="style.css" rel="stylesheet" type="text/css">
|
||||
*/
|
||||
p {
|
||||
color: purple;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
|
||||
<path fill="#000000" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 20,23.0002L 55.9998,23.0002C 57.1044,23.0002 57.9998,23.8956 57.9998,25.0002L 57.9999,46C 57.9999,47.1046 57.1045,48 55.9999,48L 41,48L 41,53L 45,53C 46.1046,53 47,53.8954 47,55L 47,57L 29,57L 29,55C 29,53.8954 29.8955,53 31,53L 35,53L 35,48L 20,48C 18.8954,48 18,47.1046 18,46L 18,25.0002C 18,23.8956 18.8954,23.0002 20,23.0002 Z M 21,26.0002L 21,45L 54.9999,45L 54.9998,26.0002L 21,26.0002 Z "/>
|
||||
</svg>
|
|
@ -0,0 +1,222 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
|
||||
/*global define, brackets, $ */
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var CommandManager = brackets.getModule("command/CommandManager"),
|
||||
MainViewManager = brackets.getModule("view/MainViewManager"),
|
||||
Commands = brackets.getModule("command/Commands"),
|
||||
Resizer = brackets.getModule("utils/Resizer"),
|
||||
StatusBar = brackets.getModule("widgets/StatusBar");
|
||||
// Orientation
|
||||
var VERTICAL_ORIENTATION = 0,
|
||||
HORIZONTAL_ORIENTATION = 1;
|
||||
// by default we use vertical orientation
|
||||
var _orientation = VERTICAL_ORIENTATION;
|
||||
// Window object reference
|
||||
var detachedWindow;
|
||||
var isReload;
|
||||
var PostMessageTransport = require("lib/PostMessageTransport");
|
||||
var Compatibility = require("lib/compatibility");
|
||||
|
||||
/*
|
||||
* Publicly avaialble function used to create an empty iframe within the second-panel
|
||||
*/
|
||||
function init() {
|
||||
//Check to see if we've created the iframe already, return if so
|
||||
if(getBrowserIframe()) {
|
||||
return;
|
||||
}
|
||||
//Get current GUI layout
|
||||
var result = MainViewManager.getLayoutScheme();
|
||||
|
||||
// If iframe does not exist, then show it
|
||||
if(result.rows === 1 && result.columns === 1) {
|
||||
show(_orientation);
|
||||
}
|
||||
/*
|
||||
*Creating the empty iFrame we'll be using
|
||||
* Starting by Emptying all contents of #second-pane
|
||||
*/
|
||||
var _panel = $("#second-pane").empty();
|
||||
|
||||
// Create the iFrame for the blob to live in later
|
||||
var iframeConfig = {
|
||||
id: "bramble-iframe-browser",
|
||||
frameborder: 0
|
||||
};
|
||||
//Append iFrame to _panel
|
||||
$("<iframe>", iframeConfig).addClass("iframeWidthHeight").appendTo(_panel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Publicly available function used to change the _orientation value of iframe-browser
|
||||
* orientation: Takes one argument of either VERTICAL_ORIENTATION OR
|
||||
* HORIZONTAL_ORIENTATION and uses that to change the _orientation value accordingly
|
||||
*/
|
||||
function setOrientation(orientation) {
|
||||
if(orientation === VERTICAL_ORIENTATION) {
|
||||
_orientation = VERTICAL_ORIENTATION;
|
||||
}
|
||||
else if (orientation === HORIZONTAL_ORIENTATION) {
|
||||
_orientation = HORIZONTAL_ORIENTATION;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Publicly available function used to change the layout of the iFrame
|
||||
* orientation: Takes one argument of either VERTICAL_ORIENTATION OR
|
||||
* HORIZONTAL_ORIENTATION and uses that to change the layout accordingly
|
||||
*/
|
||||
function show() {
|
||||
if(_orientation === VERTICAL_ORIENTATION) {
|
||||
CommandManager.execute(Commands.CMD_SPLITVIEW_VERTICAL);
|
||||
}
|
||||
else if(_orientation === HORIZONTAL_ORIENTATION) {
|
||||
CommandManager.execute(Commands.CMD_SPLITVIEW_HORIZONTAL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to interact with the second-pane,
|
||||
* In which our iFrame will exists, and the detached
|
||||
* preview, if it exist. They will be filled
|
||||
* with the url (or raw HTML) that has been passed to this function
|
||||
*/
|
||||
function update(urlOrHTML) {
|
||||
if(!urlOrHTML) {
|
||||
return;
|
||||
}
|
||||
|
||||
var iframe = getBrowserIframe();
|
||||
var doc;
|
||||
|
||||
Compatibility.supportsIFrameHTMLBlobURL(function(err, shouldUseBlobURL) {
|
||||
if(err) {
|
||||
console.error("[Brackets IFrame-Browser] Unexpected error:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(iframe) {
|
||||
if(shouldUseBlobURL) {
|
||||
iframe.src = urlOrHTML;
|
||||
} else {
|
||||
doc = iframe.contentWindow.document.open("text/html", "replace");
|
||||
doc.write(urlOrHTML);
|
||||
doc.close();
|
||||
}
|
||||
}
|
||||
|
||||
var detachedPreview = getDetachedPreview();
|
||||
if(detachedPreview) {
|
||||
isReload = true;
|
||||
if(!shouldUseBlobURL) {
|
||||
doc = detachedPreview.document.open("text/html", "replace");
|
||||
doc.write(urlOrHTML);
|
||||
doc.close();
|
||||
} else {
|
||||
detachedPreview.location.replace(urlOrHTML);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Return reference to iframe element or null if not available.
|
||||
function getBrowserIframe() {
|
||||
return document.getElementById("bramble-iframe-browser");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to hide second pane, spawn detached preview, and attach beforeunload listener
|
||||
*/
|
||||
function detachPreview() {
|
||||
var iframe = getBrowserIframe();
|
||||
|
||||
if(!iframe) {
|
||||
return;
|
||||
}
|
||||
|
||||
PostMessageTransport.reload();
|
||||
|
||||
var currentURL = iframe.src;
|
||||
// Open detached preview window
|
||||
detachedWindow = window.open(currentURL, "Preview");
|
||||
|
||||
return Compatibility.supportsIFrameHTMLBlobURL(function(err, shouldUseBlobURL) {
|
||||
if(err) {
|
||||
console.error("[Brackets IFrame-Browser] Unexpected error:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!shouldUseBlobURL) {
|
||||
var doc = detachedWindow.document.open("text/html", "replace");
|
||||
doc.write(iframe.contentWindow.document.documentElement.outerHTML);
|
||||
doc.close();
|
||||
}
|
||||
|
||||
Resizer.hide("#second-pane");
|
||||
$("#first-pane").addClass("expandEditor");
|
||||
$("#liveDevButton").removeClass("liveDevButtonDetach");
|
||||
$("#liveDevButton").addClass("liveDevButtonAttach");
|
||||
|
||||
// Adds tooltip prompting user to return to attached preview
|
||||
StatusBar.addIndicator("liveDevButtonBox", $("#liveDevButtonBox"), true, "",
|
||||
"Click to return preview to current window", "mobileViewButtonBox");
|
||||
|
||||
return detachedWindow;
|
||||
});
|
||||
}
|
||||
|
||||
// Return reference of open window if it exists and isn't closed
|
||||
function getDetachedPreview() {
|
||||
if(detachedWindow && !detachedWindow.closed) {
|
||||
return detachedWindow;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to show second pane, change lilveDevButton background and close the detached preview
|
||||
*/
|
||||
function attachPreview() {
|
||||
var detachedPreview = getDetachedPreview();
|
||||
if(detachedPreview && isReload) {
|
||||
isReload = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(detachedPreview) {
|
||||
detachedPreview.removeEventListener("beforeunload", attachPreview, false);
|
||||
detachedPreview.close();
|
||||
}
|
||||
|
||||
Resizer.show("#second-pane");
|
||||
$("#liveDevButton").removeClass("liveDevButtonAttach");
|
||||
$("#liveDevButton").addClass("liveDevButtonDetach");
|
||||
$("#first-pane").removeClass("expandEditor");
|
||||
|
||||
// Adds tooltip prompting user to detach preview
|
||||
StatusBar.addIndicator("liveDevButtonBox", $("#liveDevButtonBox"), true, "",
|
||||
"Click to open preview in separate window", "mobileViewButtonBox");
|
||||
}
|
||||
|
||||
function setListener() {
|
||||
var detachedPreview = getDetachedPreview();
|
||||
if(detachedPreview) {
|
||||
detachedPreview.addEventListener("beforeunload", attachPreview, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Define public API
|
||||
exports.init = init;
|
||||
exports.update = update;
|
||||
exports.show = show;
|
||||
exports.getBrowserIframe = getBrowserIframe;
|
||||
// Expose these constants on our module, so callers can use them with setOrientation()
|
||||
exports.HORIZONTAL_ORIENTATION = HORIZONTAL_ORIENTATION;
|
||||
exports.VERTICAL_ORIENTATION = VERTICAL_ORIENTATION;
|
||||
exports.setOrientation = setOrientation;
|
||||
exports.getDetachedPreview = getDetachedPreview;
|
||||
exports.attachPreview = attachPreview;
|
||||
exports.detachPreview = detachPreview;
|
||||
exports.setListener = setListener;
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var _launcherInstance;
|
||||
|
||||
function Launcher(options) {
|
||||
var _browser = options.browser;
|
||||
var _server = options.server;
|
||||
|
||||
Object.defineProperty(this, "browser", {
|
||||
configurable: false,
|
||||
get: function () {
|
||||
return _browser;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, "server", {
|
||||
configurable: false,
|
||||
get: function () {
|
||||
return _server;
|
||||
}
|
||||
});
|
||||
|
||||
_launcherInstance = this;
|
||||
}
|
||||
|
||||
Launcher.prototype.launch = function(url) {
|
||||
var server = this.server;
|
||||
var browser = this.browser;
|
||||
|
||||
browser.update(server.serveLiveDoc(url));
|
||||
};
|
||||
|
||||
Launcher.getCurrentInstance = function() {
|
||||
return _launcherInstance;
|
||||
};
|
||||
|
||||
// Define public API
|
||||
module.exports = Launcher;
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="76" height="76" viewBox="0 0 76.00 76.00" enable-background="new 0 0 76.00 76.00" xml:space="preserve">
|
||||
<path fill="#000000" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 29.2916,19L 46.7083,19C 48.8944,19 50.6666,20.7723 50.6666,22.9584L 50.6666,53.0417C 50.6666,55.2278 48.8944,57 46.7083,57L 29.2916,57C 27.1055,57 25.3333,55.2278 25.3333,53.0417L 25.3333,22.9584C 25.3333,20.7723 27.1055,19 29.2916,19 Z M 33.25,21.375C 32.8128,21.375 32.4583,21.7295 32.4583,22.1667L 32.4583,22.9584C 32.4583,23.3956 32.8128,23.75 33.25,23.75L 42.75,23.75C 43.1872,23.75 43.5416,23.3956 43.5416,22.9584L 43.5416,22.1667C 43.5416,21.7295 43.1872,21.375 42.75,21.375L 33.25,21.375 Z M 28.5,25.3333L 28.5,49.0833L 47.5,49.0833L 47.5,25.3333L 28.5,25.3333 Z M 38,50.6667C 36.6883,50.6667 35.625,51.73 35.625,53.0417C 35.625,54.3533 36.6883,55.4167 38,55.4167C 39.3117,55.4167 40.375,54.3533 40.375,53.0417C 40.375,51.73 39.3117,50.6667 38,50.6667 Z "/>
|
||||
</svg>
|
|
@ -0,0 +1,30 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
|
||||
/* global define, brackets */
|
||||
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var fs = brackets.getModule("filesystem/impls/filer/BracketsFiler").fs();
|
||||
var transport = require("lib/PostMessageTransport");
|
||||
|
||||
module.exports.handleRequest = function(path) {
|
||||
var response = {method: "XMLHttpRequest"};
|
||||
|
||||
// For now, we support text based requests only
|
||||
fs.readFile(path, "utf8", function(err, data) {
|
||||
if(err) {
|
||||
if(err.code === "ENOENT") {
|
||||
response.error = "No resource found for `" + path + "`";
|
||||
response.status = 404;
|
||||
} else {
|
||||
response.error = "Could not complete the request";
|
||||
response.status = 500;
|
||||
}
|
||||
} else {
|
||||
response.content = data;
|
||||
}
|
||||
|
||||
transport.send(null, JSON.stringify(response));
|
||||
});
|
||||
};
|
||||
});
|
|
@ -0,0 +1,180 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
|
||||
/* global DOMParser */
|
||||
(function(global) {
|
||||
"use strict";
|
||||
|
||||
// This script requires access to the transport to send
|
||||
// file requests to the editor as commands
|
||||
var transport = global._Brackets_LiveDev_Transport;
|
||||
|
||||
var XMLHttpRequest = global.XMLHttpRequest;
|
||||
if(!XMLHttpRequest) {
|
||||
console.error("[Brackets LiveDev] XMLHttpRequest not supported in preview");
|
||||
return;
|
||||
}
|
||||
|
||||
function sendMessage(msg) {
|
||||
if(!transport) {
|
||||
console.error("[Brackets LiveDev] No transport set");
|
||||
return;
|
||||
}
|
||||
|
||||
transport.send(JSON.stringify(msg));
|
||||
}
|
||||
|
||||
function nativeXHRFn(xhr, condition, fn, args) {
|
||||
if(condition) {
|
||||
return fn.apply(xhr, args) || true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function XMLHttpRequestLiveDev() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var requestUrl;
|
||||
var abortCalled = false;
|
||||
|
||||
var $open = xhr.open;
|
||||
// The async parameter for XHRs to the local file system
|
||||
// is ignored as we cannot support synchronous requests
|
||||
// and async is implicit
|
||||
xhr.open = function(method, url, async, user, password) {
|
||||
if(!(/\:?\/\//.test(url) || /\s*data\:/.test(url))) {
|
||||
requestUrl = url;
|
||||
abortCalled = false;
|
||||
} else {
|
||||
$open.apply(xhr, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
var $send = xhr.send;
|
||||
xhr.send = function() {
|
||||
if(!requestUrl) {
|
||||
$send.apply(xhr, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
function handleError(data) {
|
||||
xhr.status = data.status || 500;
|
||||
xhr.statusText = data.error || "Internal Server Error";
|
||||
xhr.readyState = 4;
|
||||
data.error = data.error || data;
|
||||
|
||||
if(typeof xhr.onreadystatechange === "function" && !abortCalled) {
|
||||
xhr.onreadystatechange();
|
||||
}
|
||||
|
||||
if(typeof xhr.onerror === "function" && !abortCalled) {
|
||||
return xhr.onerror(data.error);
|
||||
}
|
||||
}
|
||||
|
||||
function setResponse(data) {
|
||||
delete xhr.readyState;
|
||||
delete xhr.status;
|
||||
delete xhr.statusText;
|
||||
delete xhr.response;
|
||||
delete xhr.responseText;
|
||||
|
||||
if(data.error && !abortCalled) {
|
||||
return handleError(data);
|
||||
}
|
||||
|
||||
xhr.readyState = 4;
|
||||
xhr.status = 200;
|
||||
xhr.statusText = "OK";
|
||||
|
||||
var $responseType = xhr.responseType;
|
||||
if(!$responseType || $responseType === "") {
|
||||
$responseType = "text";
|
||||
}
|
||||
|
||||
switch($responseType) {
|
||||
case "text":
|
||||
xhr.response = data.content;
|
||||
xhr.responseText = xhr.response;
|
||||
break;
|
||||
case "document":
|
||||
xhr.response = new DOMParser(data.content, data.mimeType);
|
||||
xhr.responseText = data.content;
|
||||
break;
|
||||
case "json":
|
||||
try {
|
||||
xhr.response = JSON.parse(data.content);
|
||||
xhr.responseText = data.content;
|
||||
} catch(e) {
|
||||
handleError(e);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// TODO: We should support arraybuffers and blobs
|
||||
handleError("Response type of " + $responseType + " is not supported. Response type must be `text`, `document` or `json`.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof xhr.onreadystatechange === "function" && !abortCalled) {
|
||||
xhr.onreadystatechange();
|
||||
}
|
||||
if(typeof xhr.onload === "function" && !abortCalled) {
|
||||
// TODO: deal with addEventListener
|
||||
xhr.onload();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", function(event) {
|
||||
var data = event.data;
|
||||
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(data.method === "XMLHttpRequest") {
|
||||
setResponse(data);
|
||||
}
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
method: "XMLHttpRequest",
|
||||
path: requestUrl
|
||||
});
|
||||
|
||||
xhr.readyState = 1;
|
||||
};
|
||||
|
||||
var $abort = xhr.abort;
|
||||
xhr.abort = function() {
|
||||
if(!nativeXHRFn(xhr, !requestUrl, $abort, arguments)) {
|
||||
abortCalled = true;
|
||||
}
|
||||
};
|
||||
|
||||
var $setRequestHeader = xhr.setRequestHeader;
|
||||
xhr.setRequestHeader = function() {
|
||||
nativeXHRFn(xhr, !requestUrl, $setRequestHeader, arguments);
|
||||
};
|
||||
|
||||
var $getAllResponseHeaders = xhr.getAllResponseHeaders;
|
||||
xhr.getAllResponseHeaders = function() {
|
||||
return nativeXHRFn(xhr, !requestUrl, $getAllResponseHeaders, arguments);
|
||||
};
|
||||
|
||||
var $getResponseHeader = xhr.getResponseHeader;
|
||||
xhr.getResponseHeader = function() {
|
||||
return nativeXHRFn(xhr, !requestUrl, $getResponseHeader, arguments);
|
||||
};
|
||||
|
||||
var $overrideMimeType = xhr.overrideMimeType;
|
||||
xhr.overrideMimeType = function() {
|
||||
nativeXHRFn(xhr, !requestUrl, $overrideMimeType, arguments);
|
||||
};
|
||||
|
||||
return xhr;
|
||||
}
|
||||
|
||||
global.XMLHttpRequest = XMLHttpRequestLiveDev;
|
||||
|
||||
}(this));
|
|
@ -0,0 +1,383 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4,
|
||||
maxerr: 50, browser: true */
|
||||
/*global define, brackets, $ */
|
||||
|
||||
/**
|
||||
* This extension provides in-editor livepreview through an iframe,
|
||||
* and leverages the experimental Multi Browser implementation of brackets
|
||||
* (see https://github.com/adobe/brackets/tree/master/src/LiveDevelopment/MultiBrowserImpl)
|
||||
*/
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
// Load dependencies
|
||||
var AppInit = brackets.getModule("utils/AppInit"),
|
||||
CommandManager = brackets.getModule("command/CommandManager"),
|
||||
Commands = brackets.getModule("command/Commands"),
|
||||
EditorManager = brackets.getModule("editor/EditorManager"),
|
||||
LiveDevServerManager = brackets.getModule("LiveDevelopment/LiveDevServerManager"),
|
||||
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
|
||||
ProjectManager = brackets.getModule("project/ProjectManager"),
|
||||
LiveDevelopment = brackets.getModule("LiveDevelopment/LiveDevMultiBrowser"),
|
||||
UrlParams = brackets.getModule("utils/UrlParams").UrlParams,
|
||||
Editor = brackets.getModule("editor/Editor").Editor,
|
||||
Rewriter = brackets.getModule("filesystem/impls/filer/lib/HTMLRewriter"),
|
||||
// Load nohost dependencies
|
||||
Browser = require("lib/iframe-browser"),
|
||||
UI = require("lib/UI"),
|
||||
Launcher = require("lib/launcher"),
|
||||
HTMLServer = require("nohost/src/HTMLServer").HTMLServer,
|
||||
StaticServer = require("nohost/src/StaticServer").StaticServer,
|
||||
ExtensionUtils = brackets.getModule("utils/ExtensionUtils"),
|
||||
PostMessageTransport = require("lib/PostMessageTransport"),
|
||||
FileSystem = brackets.getModule("filesystem/FileSystem"),
|
||||
Path = brackets.getModule("filesystem/impls/filer/BracketsFiler").Path,
|
||||
BlobUtils = brackets.getModule("filesystem/impls/filer/BlobUtils"),
|
||||
XHRHandler = require("lib/xhr/XHRHandler");
|
||||
|
||||
ExtensionUtils.loadStyleSheet(module, "stylesheets/style.css");
|
||||
|
||||
var _HTMLServer,
|
||||
_staticServer,
|
||||
codeMirror,
|
||||
parentWindow = window.parent,
|
||||
params = new UrlParams();
|
||||
|
||||
// Load initial document
|
||||
var defaultHTML = brackets.getModule("text!filesystem/impls/filer/lib/default.html");
|
||||
var defaultCSS = require("text!lib/default-files/style.css");
|
||||
var defaultJS = require("text!lib/default-files/script.txt");
|
||||
|
||||
// Force entry to if statments on line 262 of brackets.js to create
|
||||
// a new project
|
||||
PreferencesManager.setViewState("afterFirstLaunch", false);
|
||||
params.remove("skipSampleProjectLoad");
|
||||
|
||||
// Server for HTML files only
|
||||
function _getHTMLServer() {
|
||||
if (!_HTMLServer) {
|
||||
_HTMLServer = new HTMLServer({
|
||||
pathResolver : ProjectManager.makeProjectRelativeIfPossible,
|
||||
root : ProjectManager.getProjectRoot()
|
||||
});
|
||||
}
|
||||
return _HTMLServer;
|
||||
}
|
||||
|
||||
// Server for non-HTML files only
|
||||
function _getStaticServer() {
|
||||
if (!_staticServer) {
|
||||
_staticServer = new StaticServer({
|
||||
pathResolver : ProjectManager.makeProjectRelativeIfPossible,
|
||||
root : ProjectManager.getProjectRoot()
|
||||
});
|
||||
}
|
||||
return _staticServer;
|
||||
}
|
||||
|
||||
function parseData(data, deferred) {
|
||||
var dataReceived = data;
|
||||
|
||||
try {
|
||||
data = dataReceived || null;
|
||||
data = JSON.parse(data);
|
||||
data = data || {};
|
||||
} catch(err) {
|
||||
// Quick fix: Ignore the 'process-tick' message being sent
|
||||
if(dataReceived === "process-tick") {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.error("Parsing message from thimble failed: ", err);
|
||||
|
||||
if(deferred) {
|
||||
deferred.reject();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function handleMessage(message) {
|
||||
var currentDocUrl = Browser.getBrowserIframe().src;
|
||||
var currentDocPath = BlobUtils.getFilename(currentDocUrl);
|
||||
var currentDir = currentDocPath !== currentDocUrl ? Path.dirname(currentDocPath) : currentDocPath;
|
||||
var requestedPath;
|
||||
|
||||
try {
|
||||
message = parseData(message);
|
||||
} catch(ex) {
|
||||
console.error("[Brackets Browser LiveDev Error] Cannot handle message ", message);
|
||||
return;
|
||||
}
|
||||
|
||||
if(message.method === "XMLHttpRequest") {
|
||||
requestedPath = Path.resolve(currentDir, Path.normalize(message.path));
|
||||
XHRHandler.handleRequest(requestedPath);
|
||||
}
|
||||
}
|
||||
|
||||
// We wait until the LiveDevelopment module is initialized and the project loaded
|
||||
// so we can safely swap our transport and launcher modules for
|
||||
// the defaults and start LiveDev.
|
||||
function _configureLiveDev() {
|
||||
// Turn preview iFrame On
|
||||
Browser.init();
|
||||
|
||||
function _configureModules() {
|
||||
// Set up our transport and plug it into live-dev
|
||||
PostMessageTransport.setIframe(Browser.getBrowserIframe());
|
||||
LiveDevelopment.setTransport(PostMessageTransport);
|
||||
|
||||
// Set up our launcher in a similar manner
|
||||
LiveDevelopment.setLauncher(new Launcher({
|
||||
browser: Browser,
|
||||
server: _getHTMLServer()
|
||||
}));
|
||||
|
||||
LiveDevelopment.open();
|
||||
}
|
||||
LiveDevelopment.one("statusChange", _configureModules);
|
||||
}
|
||||
ProjectManager.one("projectOpen", _configureLiveDev);
|
||||
|
||||
/*
|
||||
* This function is attached to the window as an event listener
|
||||
* Its purpose is to intercept post messages from bramble proxy in thimble
|
||||
* some of these being:
|
||||
* undo, redo, size changer, or any other buttons relating to menu or view
|
||||
* within event we expect to receive a JSONable object that contains a commandCategory:
|
||||
* menuCommand: "Menu Command" relating to menu commands runable
|
||||
* fontChange: refers to a method we use to change fonts size
|
||||
* editorCommand: "Editor Command" relating to functions relating ot the editor itself
|
||||
* also contains a variable of "params" which can be used to send further information needed
|
||||
*/
|
||||
function _buttonListener(event) {
|
||||
var msgObj;
|
||||
var i;
|
||||
try {
|
||||
msgObj = JSON.parse(event.data);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(msgObj.commandCategory === "menuCommand"){
|
||||
codeMirror.focus();
|
||||
CommandManager.execute(Commands[msgObj.command]);
|
||||
}
|
||||
else if (msgObj.commandCategory === "fontChange") {
|
||||
CommandManager.execute(Commands[msgObj.command]);
|
||||
if(msgObj.params < "12") {
|
||||
for(i = 12; i > msgObj.params; i--) {
|
||||
CommandManager.execute(Commands.VIEW_DECREASE_FONT_SIZE);
|
||||
}
|
||||
}
|
||||
else if(msgObj.params > "12") {
|
||||
for(i = 12; i < msgObj.params; i++) {
|
||||
CommandManager.execute(Commands.VIEW_INCREASE_FONT_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msgObj.commandCategory === "editorCommand") {
|
||||
Editor[msgObj.command](msgObj.params);
|
||||
}
|
||||
else if (msgObj.commandCategory === "reloadCommand") {
|
||||
PostMessageTransport.reload();
|
||||
}
|
||||
else if (msgObj.commandCategory === "runJavascript") {
|
||||
if(msgObj.command) {
|
||||
Rewriter.enableScripts();
|
||||
}
|
||||
else {
|
||||
Rewriter.disableScripts();
|
||||
}
|
||||
PostMessageTransport.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// We configure Brackets to run the experimental live dev
|
||||
// with our nohost server and iframe combination. This has to
|
||||
// occur before the project is loaded, triggering the start of
|
||||
// the live preview.
|
||||
AppInit.extensionsLoaded(function () {
|
||||
// Flip livedev.multibrowser to true
|
||||
var prefs = PreferencesManager.getExtensionPrefs("livedev");
|
||||
prefs.set("multibrowser", true);
|
||||
|
||||
ExtensionUtils.loadStyleSheet(module, "stylesheets/tutorials.css");
|
||||
|
||||
// Register servers with highest priority
|
||||
LiveDevServerManager.registerServer({ create: _getStaticServer }, 9000);
|
||||
LiveDevServerManager.registerServer({ create: _getHTMLServer }, 9001);
|
||||
});
|
||||
|
||||
AppInit.appReady(function (){
|
||||
function attachListeners() {
|
||||
parentWindow.postMessage(JSON.stringify({
|
||||
type: "bramble:loaded"
|
||||
}), "*");
|
||||
|
||||
// Below are methods to change the preferences of brackets, more available at:
|
||||
// https://github.com/adobe/brackets/wiki/How-to-Use-Brackets#list-of-supported-preferences
|
||||
PreferencesManager.set("insertHintOnTab", true);
|
||||
// Make the spaceUnits and tabSize consistent
|
||||
PreferencesManager.set("spaceUnits", 2);
|
||||
PreferencesManager.set("tabSize", 2);
|
||||
// Allows the closeTags to indent consistently
|
||||
PreferencesManager.set("closeTags", true);
|
||||
|
||||
// Once the app has loaded our file,
|
||||
// and we can be confident the editor is open,
|
||||
// get a reference to it and attach our "onchange"
|
||||
// listener to codemirror
|
||||
codeMirror = EditorManager.getActiveEditor()._codeMirror;
|
||||
|
||||
parentWindow.postMessage(JSON.stringify({
|
||||
type: "bramble:change",
|
||||
sourceCode: codeMirror.getValue(),
|
||||
lastLine: codeMirror.lastLine(),
|
||||
scrollInfo: codeMirror.getScrollInfo()
|
||||
}), "*");
|
||||
|
||||
codeMirror.on("change", function(){
|
||||
parentWindow.postMessage(JSON.stringify({
|
||||
type: "bramble:change",
|
||||
sourceCode: codeMirror.getValue(),
|
||||
lastLine: codeMirror.lastLine()
|
||||
}), "*");
|
||||
});
|
||||
|
||||
codeMirror.on("viewportChange", function() {
|
||||
parentWindow.postMessage(JSON.stringify({
|
||||
type: "bramble:viewportChange",
|
||||
scrollInfo: codeMirror.getScrollInfo()
|
||||
}), "*");
|
||||
});
|
||||
|
||||
window.addEventListener("message", function(e) {
|
||||
var data = parseData(e.data);
|
||||
var value;
|
||||
var mark;
|
||||
var type;
|
||||
|
||||
if(!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
type = data.type;
|
||||
|
||||
if(type === "message") {
|
||||
handleMessage(data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if(type !== "bramble:edit") {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!data.fn) {
|
||||
console.error("No edit function sent from thimble to call on code mirror");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// QuickFix: Hack to create a DOM element as a marker since it cannot
|
||||
// be passed in through postMessage as JSON's stringify cannot work for
|
||||
// DOM elements (because it has circular references)
|
||||
if(data.fn === "setGutterMarker" && data.params[2]) {
|
||||
mark = document.createElement(data.params[2].name);
|
||||
var attributes = data.params[2].attributes;
|
||||
Object.keys(attributes).forEach(function(attrName) {
|
||||
$(mark).attr(attrName, attributes[attrName]);
|
||||
});
|
||||
mark.innerHTML = data.params[2].innerHTML;
|
||||
data.params[2] = mark;
|
||||
}
|
||||
|
||||
if(data.fn === "getLineHeight") {
|
||||
var codeMirrorLine = document.querySelector(data.params[0]);
|
||||
value = parseFloat(window.getComputedStyle(codeMirrorLine).height);
|
||||
} else {
|
||||
value = codeMirror[data.fn].apply(codeMirror, data.params);
|
||||
}
|
||||
|
||||
if(value === undefined || value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
parentWindow.postMessage(JSON.stringify({
|
||||
type: "bramble:edit",
|
||||
fn: data.fn,
|
||||
value: typeof value !== "object" ? value : undefined
|
||||
}), "*");
|
||||
});
|
||||
}
|
||||
|
||||
// When the app is loaded and ready, hide the menus/toolbars
|
||||
UI.initUI(attachListeners);
|
||||
});
|
||||
|
||||
// We listen for a message from Thimble containing
|
||||
// the make's initial code.
|
||||
// For now, we have a default html make for testing
|
||||
// with just Brackets.
|
||||
exports.initExtension = function() {
|
||||
var deferred = new $.Deferred();
|
||||
|
||||
function _getInitialDocument(e) {
|
||||
var data = parseData(e.data, deferred);
|
||||
|
||||
// Remove the listener after we confirm the event is the
|
||||
// one we're waiting for
|
||||
if (!data || data.type !== "bramble:init") {
|
||||
return;
|
||||
}
|
||||
|
||||
window.removeEventListener("message", _getInitialDocument);
|
||||
|
||||
window.addEventListener("message", _buttonListener);
|
||||
|
||||
var fileHTML = FileSystem.getFileForPath("/index.html");
|
||||
var fileCSS = FileSystem.getFileForPath("/style.css");
|
||||
var fileJS = FileSystem.getFileForPath("/script.js");
|
||||
|
||||
// Write the HTML file and block on it being done.
|
||||
fileHTML.write(data.source ? data.source : defaultHTML,
|
||||
function(err) {
|
||||
if (err) {
|
||||
deferred.reject();
|
||||
return;
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
}
|
||||
);
|
||||
|
||||
// Write the CSS and JS file without blocking.
|
||||
fileCSS.write(defaultCSS, function(err) {
|
||||
if (err) {
|
||||
console.error("Couldn't write /style.css");
|
||||
return;
|
||||
}
|
||||
|
||||
fileJS.write(defaultJS, function(err) {
|
||||
if (err) {
|
||||
console.error("Couldn't write /script.js");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("message", _getInitialDocument);
|
||||
|
||||
// Signal to thimble that we're waiting for the
|
||||
// initial make source code
|
||||
window.parent.postMessage(JSON.stringify({
|
||||
type: "bramble:init"
|
||||
}), "*");
|
||||
|
||||
return deferred.promise();
|
||||
};
|
||||
});
|
|
@ -0,0 +1,104 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
|
||||
/*global define, brackets */
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var BaseServer = brackets.getModule("LiveDevelopment/Servers/BaseServer").BaseServer,
|
||||
LiveDevelopmentUtils = brackets.getModule("LiveDevelopment/LiveDevelopmentUtils"),
|
||||
BlobUtils = brackets.getModule("filesystem/impls/filer/BlobUtils"),
|
||||
Filer = brackets.getModule("filesystem/impls/filer/BracketsFiler"),
|
||||
Rewriter = brackets.getModule("filesystem/impls/filer/lib/HTMLRewriter"),
|
||||
Compatibility = require("lib/compatibility"),
|
||||
_shouldUseBlobURL;
|
||||
|
||||
function HTMLServer(config) {
|
||||
config = config || {};
|
||||
BaseServer.call(this, config);
|
||||
}
|
||||
|
||||
HTMLServer.prototype = Object.create(BaseServer.prototype);
|
||||
HTMLServer.prototype.constructor = HTMLServer;
|
||||
|
||||
//Returns a pre-generated blob url based on path
|
||||
HTMLServer.prototype.pathToUrl = function(path) {
|
||||
return BlobUtils.getUrl(path);
|
||||
};
|
||||
//Returns a path based on blob url
|
||||
HTMLServer.prototype.urlToPath = function(url) {
|
||||
return BlobUtils.getFilename(url);
|
||||
};
|
||||
|
||||
HTMLServer.prototype.start = function() {
|
||||
this.fs = Filer.fs();
|
||||
};
|
||||
|
||||
HTMLServer.prototype.stop = function() {
|
||||
this.fs = null;
|
||||
};
|
||||
|
||||
HTMLServer.prototype.readyToServe = function () {
|
||||
var self = this;
|
||||
var deferred = new $.Deferred();
|
||||
|
||||
// Decide if we can use Blob URLs or need to document.write()
|
||||
Compatibility.supportsIFrameHTMLBlobURL(function(err, shouldUseBlobURL) {
|
||||
if(err) {
|
||||
console.error("[Brackets HTMLServer] Unexpected error:", err);
|
||||
deferred.reject();
|
||||
}
|
||||
|
||||
_shouldUseBlobURL = shouldUseBlobURL;
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if this server can serve local file. LiveDevServerManager
|
||||
* calls this method when determining if a server can serve a file.
|
||||
* @param {string} localPath A local path to file being served.
|
||||
* @return {boolean} true When the file can be served, otherwise false.
|
||||
*/
|
||||
HTMLServer.prototype.canServe = function (localPath) {
|
||||
// If we can't transform the local path to a project relative path,
|
||||
// the path cannot be served
|
||||
if (localPath === this._pathResolver(localPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Url ending in "/" implies default file, which is usually index.html.
|
||||
// Return true to indicate that we can serve it.
|
||||
if (localPath.match(/\/$/)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return LiveDevelopmentUtils.isStaticHtmlFileExt(localPath);
|
||||
};
|
||||
|
||||
/**
|
||||
* When a livedocument is added to the server cache, make sure live
|
||||
* instrumentation is enabled
|
||||
*/
|
||||
HTMLServer.prototype.add = function (liveDocument) {
|
||||
if (liveDocument.setInstrumentationEnabled) {
|
||||
// enable instrumentation
|
||||
liveDocument.setInstrumentationEnabled(true);
|
||||
}
|
||||
BaseServer.prototype.add.call(this, liveDocument);
|
||||
};
|
||||
|
||||
/**
|
||||
* If a livedoc exists, serves the instrumented version of the file.
|
||||
* We either serve raw HTML or a Blob URL depending on browser compatibility.
|
||||
*/
|
||||
HTMLServer.prototype.serveLiveDoc = function(url) {
|
||||
var path = BlobUtils.getFilename(url);
|
||||
var liveDocument = this._liveDocuments[path];
|
||||
var html = Rewriter.rewrite(path, liveDocument.getResponseData().body);
|
||||
|
||||
return _shouldUseBlobURL ? BlobUtils.createURL(path, html, "text/html") : html;
|
||||
};
|
||||
|
||||
exports.HTMLServer = HTMLServer;
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
|
||||
/*global define, brackets */
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var BaseServer = brackets.getModule("LiveDevelopment/Servers/BaseServer").BaseServer,
|
||||
BlobUtils = brackets.getModule("filesystem/impls/filer/BlobUtils"),
|
||||
Filer = brackets.getModule("filesystem/impls/filer/BracketsFiler");
|
||||
|
||||
function StaticServer(config) {
|
||||
config = config || {};
|
||||
BaseServer.call(this, config);
|
||||
}
|
||||
|
||||
StaticServer.prototype = Object.create(BaseServer.prototype);
|
||||
StaticServer.prototype.constructor = StaticServer;
|
||||
|
||||
//Returns a pre-generated blob url based on path
|
||||
StaticServer.prototype.pathToUrl = function(path) {
|
||||
return BlobUtils.getUrl(path);
|
||||
};
|
||||
//Returns a path based on blob url
|
||||
StaticServer.prototype.urlToPath = function(url) {
|
||||
return BlobUtils.getFilename(url);
|
||||
};
|
||||
|
||||
StaticServer.prototype.start = function() {
|
||||
this.fs = Filer.fs();
|
||||
};
|
||||
|
||||
StaticServer.prototype.stop = function() {
|
||||
this.fs = null;
|
||||
};
|
||||
|
||||
exports.StaticServer = StaticServer;
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "Brackets.BrowserLiveDev",
|
||||
"title": "Brackets In Browser Live Development",
|
||||
"description": "Allow Live Development when running Brackets in a Browser",
|
||||
"homepage": "https://github.com/humphd/brackets-browser-livedev",
|
||||
"version": "0.0.1",
|
||||
"author": "@humphd",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"brackets": ">=0.44.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/* Statusbar buttons start */
|
||||
.mobileButton {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-image: url("../lib/mobileButton.svg");
|
||||
background-size: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.desktopButton {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-image: url("../lib/desktopButton.svg");
|
||||
background-size: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#liveDevButton {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-size: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.liveDevButtonDetach {
|
||||
background-image: url("../lib/appbar.new.window.svg");
|
||||
}
|
||||
.liveDevButtonAttach {
|
||||
background-image: url("../lib/appbar.fullscreen.box.svg");
|
||||
}
|
||||
|
||||
.second-pane-scroll {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
|
||||
.expandEditor {
|
||||
width: 100% !important;
|
||||
}
|
||||
/* Statusbar buttons end */
|
||||
|
||||
/* Mobile view start */
|
||||
.phone-container {
|
||||
margin: 0px auto;
|
||||
margin-top: 30px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border: 2px solid #555;
|
||||
width: 330px;
|
||||
border-radius: 20px;
|
||||
background: #222 none repeat scroll 0% 0%;
|
||||
}
|
||||
|
||||
.phone-inner {
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.phone-top {
|
||||
height: 55px;
|
||||
width: 100%;
|
||||
border-radius: 6px 6px 0px 0px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.phone-border {
|
||||
background: #FFF none repeat scroll 0% 0%;
|
||||
border: 2px solid #555;
|
||||
border-radius: 2px;
|
||||
width: 310px;
|
||||
margin: 0px auto;
|
||||
height: 480px;
|
||||
}
|
||||
|
||||
.phone-shadow {
|
||||
background-color: #333;
|
||||
height: 50px;
|
||||
width: 340px;
|
||||
border-radius: 25px;
|
||||
position: absolute;
|
||||
bottom: -26px;
|
||||
left: -5px;
|
||||
opacity: 0.15;
|
||||
transform: scaleY(0.3);
|
||||
}
|
||||
|
||||
.phone-bottom::after {
|
||||
content: "";
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
position: absolute;
|
||||
left: calc(50% - 18px);
|
||||
border-radius: 30px;
|
||||
bottom: 13px;
|
||||
border: 3px solid #444;
|
||||
}
|
||||
|
||||
.phone-bottom {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
border-radius: 0px 0px 6px 6px;
|
||||
}
|
||||
|
||||
.phone-container .phone-body {
|
||||
height: 480px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.phone-body {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: #FFF none repeat scroll 0% 0%;
|
||||
height: 100%;
|
||||
}
|
||||
/* Mobile view end */
|
||||
|
||||
/* Hide UI start */
|
||||
.hideLeftToolbar {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.editor-holder-height {
|
||||
height: 96%;
|
||||
}
|
||||
|
||||
.first-pane-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hideRightToolbar {
|
||||
right: 0 !important;
|
||||
}
|
||||
/* Hide UI end */
|
||||
|
||||
/* Iframe start */
|
||||
.iframeWidthHeight {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
/* Iframe end */
|
|
@ -0,0 +1,3 @@
|
|||
.tutorial-highlight {
|
||||
background-color: #AACBF6;
|
||||
}
|
|
@ -28,7 +28,7 @@ define(function (require, exports, module) {
|
|||
"UrlCodeHints",
|
||||
|
||||
// Custom extensions we want loaded by default
|
||||
"brackets-browser-livedev",
|
||||
"bramble",
|
||||
"brackets-paste-and-indent"
|
||||
];
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче